Categories:

Bringing our CSS clock to life using JavaScript's requestAnimationFrame() method

As JavaScript enthusiasts, most of us probably have at some point created a live time display in JavaScript and watched with glee as it accessed the actual time on our computer and showed it. Not to trivialize those accomplishments, that process undoubtedly involved creating a new instance of the Date() object and repeatedly looking up the value of the various time fields inside a setTimeout or setInterval method. Well, that same time tested process is still relevant for the most part when it comes to breathing life into our modern CSS clock variant, except we'll be converting each time field's value (ie: 13hr) into degrees, and for performance sake, ditch setInterval for the more efficient requestAnimationFrame() method.

Setting the clock hands using JavaScript/ jQuery

To set the clock hands in JavaScript, we first express each time field in terms of degrees, then set the CSS3 property transform:rotate(deg) on each hand to that degree to rotate it. We'll turn to jQuery to do this, as it automatically takes care of accessing the supported version of the transform property in the user's browser, saving us from the drudgery of dealing with those vendor prefixes ourselves.

To illustrate, lets create a JavaScript function that manually sets the time on our CSS clock to anything you want, by converting each time unit (ie: 12hr) to degrees:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

var $hands = $('#staticclock div.hand') // reference all the hand DIVs

function setclock(fieldval, fieldtype){
 if (fieldtype == 'hrfield'){ //fieldval should be 0-23
  var hour_as_degree = fieldval / 12 * 360 //express hours in degs
  $hands.filter('.hour').css({transform: 'rotate(' + hour_as_degree + 'deg)' }) //rotate hour DIV x degrees
 }
 else if (fieldtype == 'minfield'){ //fieldval should be 0-59
  var minute_as_degree = fieldval / 60 * 360 // same deal in mins
  $hands.filter('.minute').css({transform: 'rotate(' + minute_as_degree + 'deg)' })
 }
 else if (fieldtype == 'secfield'){ //fieldval should be 0-59
  var second_as_degree = fieldval /60 * 360
  $hands.filter('.second').css({transform: 'rotate(' + second_as_degree + 'deg)' })
 }
}

So to set the CSS clock to 13hr:35min:12sec for example, we'd call setclock() as follows:

setclock(13, 'hrfield')
setclock(35, 'minfield')
setclock(12, 'secfield')

If you examine the function, you'll see how the conversion from conventional time unit to degrees occurs. For the hour field, we divide the hour (ie: 13hr) by 12 to first get it as a fraction of a full 12 hr cycle, then multiple that by 360 to get the final value as a fraction of 360 deg. The same principle goes behind the minute and second fields, except we divide by 60 instead of 12.

Just for fun, the following uses the above function plus a form to let you set the desired time on the CSS clock visually:

Demo:

 

hrs Please enter number between 0-23
mins Please enter number between 0-59
secs Please enter number between 0-59


The time has arrived...

With our good grasp now on the various components that make up our CSS clock and how to get its hands to do our bidding, we're ready to make the final plunge of bringing it to life. We'll add two refining tweaks to the final clock to uphold our reputation as masters of our trade! They are:

  • Modify the Math used so the hour and second fields are more accurate than just to the nearest hour and second. Instead of just returning whole integers, we'll make them return floating numbers that take into account the current minute and millisecond, respectively. When the clock is shown then, the two hands will animate with finer incremental steps.
  • Ditch the old setInterval() method of JavaScript for the less fallible requestAnimationFrame() method of repeatedly calling a function to update our clock. requestAnimationFrame() is much more predictable in how often it updates itself compared to the former, minimizing frame skipping and better adapting to available system resources. More on this below.

Without further adieu then, here is our modest script that turns Pinocchio into a real boy!

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

<script type="text/javascript">

var $hands = $('#liveclock div.hand')

window.requestAnimationFrame = window.requestAnimationFrame
                               || window.mozRequestAnimationFrame
                               || window.webkitRequestAnimationFrame
                               || window.msRequestAnimationFrame
                               || function(f){return setTimeout(f, 1000/60)}


function updateclock(){
 var curdate = new Date()
 var hour_as_degree = ( curdate.getHours() + curdate.getMinutes()/60 ) / 12 * 360
 var minute_as_degree = curdate.getMinutes() / 60 * 360
 var second_as_degree = ( curdate.getSeconds() + curdate.getMilliseconds()/1000 ) /60 * 360
 $hands.filter('.hour').css({transform: 'rotate(' + hour_as_degree + 'deg)' })
 $hands.filter('.minute').css({transform: 'rotate(' + minute_as_degree + 'deg)' })
 $hands.filter('.second').css({transform: 'rotate(' + second_as_degree + 'deg)' })
 requestAnimationFrame(updateclock) //
}

requestAnimationFrame(updateclock)


</script>

Result (Click here to see the clock on a separate page):

 

Right near the top of the code you see the reference to the method requestAnimationFrame, which simply put executes the function passed into it once just like with setTimeout(), except instead of after the user specified delay, it does this automatically just before the next available screen repaint, which based on a typical device's 60 frames per second screen refresh rate would mean just before 16.7 milliseconds (1000/60) or so. In other words, requestAnimationFrame lets us run any function automatically as soon as the browser is able to repaint the screen, compared to setTimeout's user defined delay. So far so good?

When requestAnimationFrame is called recursively, such as inside the function we pass into it, it behaves a lot like setInterval, except it repeats itself not based on the user defined interval, but once again, at every next available screen repaint. That's what's happening in the code above. So what's the point you're already asking. Well, when we're running JavaScript code that paints to the screen (ie: in an animation), when the browser is able to repaint the screen will differ greatly based on system resources and frame refresh rate, and relying on setInterval()'s interval parameter becomes meaningless, even inefficient if the browser tries to execute your code before it's able to show it on the screen. requestAnimationFrame takes care of this issue by repeating itself only when the user's computer is ready for a screen repaint, and furthermore, pauses the animation when the page isn't visible (which on mobile devices conserves battery). All together this leads to a much smoother, consistent animation experience to the user.

requestAnimationFrame is supported in IE10+, FF4+, Chrome 10+, and Opera 15+. For browsers that don't recognize any variant of requestAnimationFrame in the form of vendor prefixes, we fall back to the good old setTimeout method, as illustrated in our code:

window.requestAnimationFrame = window.requestAnimationFrame
                               || window.mozRequestAnimationFrame
                               || window.webkitRequestAnimationFrame
                               || window.msRequestAnimationFrame
                               || function(f){return setTimeout(f, 1000/60)} // roughly 60 frames per second

Inside the fallback setTimeout code, notice how we've set the delay value to 1000/60, or roughly 60 invocations per second. As available system resources rise or fall that delay value becomes meaningless, unlike with the actual requestAnimationFrame method, but it's a noble attempt nonetheless.

Lets move on to the updateclock() function now. To make the return values for the hour and second hands accurate to the minute and milliseconds, respectively, we include the current minutes and milliseconds when calculating the equivalent degrees for these two fields (see code in orange). The result is clearly visible with the seconds hand as it moves in an unabated manner around the clock.

Our adventures into CSS3 clock making ends here, but may it open up the possibilities for you for creating even more elaborate crafts using CSS!

End of Tutorial