An analogue clock using only CSS

Hav­ing read the blurb around Safari’s CSS tran­si­tions I opted to famil­iar­ize myself with a quick project — the aim of which was to cre­ate a func­tional, CSS only, ana­logue clock.

Result

Exper­i­ment: CSS Ana­logue Clock
Exper­i­ment works in Safari 4 Beta and Google Chrome. A work­ing clock that option­ally resorts to JavaScript to grab the cur­rent time (can be achieved by other means).

How To

Before get­ting into the nitty gritty I cre­ated four images, a clock face and three trans­par­ent PNG hands (sec­onds, min­utes and hours), ensur­ing that each of these were the same size so that when over­layed their cen­tres would align. The HTML and CSS to get us going is as follows:

<div id="clock">
	<div id="hour"><img src="images/hourHand.png" /></div>
	<div id="minute"><img src="images/minuteHand.png" /></div>
	<div id="second"><img src="images/secondHand.png" /></div>
</div>
#clock {
position: relative;
width: 378px;
height: 378px;
background-image: url('../images/clockFace.png');
left: 50%;
margin: 5em 0 0 -189px;
}

#clock div {
position: absolute;
}

The magic that rotates the clock’s hands comes via two WebKit spe­cific CSS prop­er­ties, -webkit-transition (doc­u­men­ta­tion) and -webkit-transform (doc­u­men­ta­tion). The trans­form prop­erty can alter the appear­ance of an ele­ment via a two dimen­sional trans­for­ma­tion, for instance: scal­ing, rotat­ing and skew­ing a DIV ele­ment. In this case it is used to rotate the clock hands to the cor­rect angles; the CSS below puts the hour hand at 3 o’clock:

#clock img[src*='hour'] {
-webkit-transform: rotate(90deg);
}

The tran­si­tion prop­erty cre­ates an ani­ma­tion of a spec­i­fied prop­erty between two val­ues when trig­gered, for instance fad­ing the opac­ity on a DIV ele­ment from 1 to 0 — trig­gered using the :hover pseudo class. Tran­si­tion dura­tion and the tran­si­tion tim­ing func­tion (e.g. lin­ear) should also be set, amongst other optional prop­er­ties. In this exam­ple the tran­si­tion is from one trans­for­ma­tion angle to another with dura­tions that match the appro­pri­ate clock hand, so the sec­ond hand takes 60 sec­onds to com­plete a 360 degree rota­tion. The tran­si­tion is trig­gered using the :tar­get pseudo ele­ment — if the URI con­tains the ‘clock’ frag­ment then the time piece shall start ticking.

#clock img[src*='second'] {
/* -webkit-transition: property duration timing-function */
-webkit-transition: -webkit-transform 60s linear;
}

#clock:target img[src*='second'] {
-webkit-transform: rotate(360deg);
}

The above tran­si­tion lasts only one rota­tion but by alter­ing the dura­tion length and degree of rota­tion in accor­dance the sec­ond hand can keep on going (e.g. 600 sec­onds and 3600 degrees rota­tion gives a bat­tery life of 10 min­utes), a fairly safe assump­tion that users will not stay on the page for too long.

#clock img[src*='second'] {
-webkit-transition: -webkit-transform 600000s linear;
}

#clock:target img[src*='second'] {
-webkit-transform: rotate(3600000deg);
}

#clock img[src*='minute'] {
-webkit-transition: -webkit-transform 360000s linear;
}

#clock:target img[src*='minute'] {
-webkit-transform: rotate(36000deg);
}

Grab the cur­rent time

Although the ani­ma­tion works beau­ti­fully, CSS alone is not capa­ble of obtain­ing the cur­rent time. To start the clock at the cor­rect time a dynamic trans­for­ma­tion needs to be applied to the clock hand con­tain­ers, this is eas­i­est done with inline styles and can be set in any num­ber of ways by the back­end when the page loads, thereby erad­i­cat­ing any need for JavaScript.

Alter­na­tively, if you’ve no objec­tions to using JavaScript, I’ve cre­ated a small startClock() func­tion to do the job (albeit using Pro­to­type 1.6.0.3 for my own con­ve­nience):

function startClock() {
	var angle = 360/60;
	var date = new Date();
	var hour = date.getHours();
	if(hour > 12) {
		hour = hour - 12;
	}
	var minute = date.getMinutes();
	var second = date.getSeconds();
	var hourAngle = (360/12)*hour + (360/(12*60))*minute;
	$('minute').setStyle('-webkit-transform: rotate('+angle*minute+'deg)');
	$('second').setStyle('-webkit-transform: rotate('+angle*second+'deg)');
	$('hour').setStyle('-webkit-transform: rotate('+hourAngle+'deg)');
}

A word of warn­ing — apply­ing the inline style directly to the image will over­ride the tran­si­tion effects defined in the CSS file.

16 Comments

  1. Jose

    In Google Chromium (for linux) works fine!

  2. John Giotta

    You can add Google Chrome to your com­pat­i­ble browsers

  3. Dever

    Works in Safari 3.2.1 on a Mac, cool !

  4. david
  5. Mem's

    This work also on Safari 3.1.2 (525.21)

  6. Scavenger

    Cool!

    Page zoom­ing doesn’t work on Mac (Safari 3.2.1), though.

  7. Zachary Abresch

    Sweet! Works in Safari 4 BETA. I’m going to try this out myself!

  8. James Urquhart
  9. Val

    Very cool! Gotta be care­ful with this stuff, tho — when I ran it in Chrome on WinXP on a Dell Insp­iron 6000, it chewed up 55% of CPU.

  10. wilq32

    You could try to inte­grate rotat­ing for older browsers by using: http://​wilq32​.googlepages​.com/​w​i​l​q​3​2​.​r​o​l​l​i​m​a​ge222

  11. Ryan Cannon

    I used a sim­i­lar tech­nique here:

    http://​ryan​can​non​.com/​m​t​g/hi/

    to keep time, but mine is intended for longer peri­ods. One thing to note: you must always incre­ment the trans­form up. If, for exam­ple, you set the hands at noon to 0deg when they were at 359deg a minute before hand, your arms will rotate backwards.

    A bit of a pain, and I haven’t found a work around, other than incre­ment­ing to infinity.

  12. Matt

    You get an inter­est­ing effect when you try and select the image in chrome, crazy shapes!

  13. Ben

    This is amaz­ing! Had no idea you could do any­thing like this in CSS, will have to give it a go!

  14. Java developer

    looks really great.

  15. premkumar

    i need full codings

  16. omeng

    rotat­ing but not in the cur­rent time. its not read­ing the java. please help.