Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » HTML / CSS » HTML » Revisions
 

Lazy HTML5 Digital Clock with Canvas and JSON

, 14 Apr 2012
Rate this:
Please Sign up or sign in to vote.
Lazy HTML5 Digital Clock is a Lazy Drawing & Optimized clock based on HTML5 canvas for rendering the time and JSON for Lazy Drawing. The clock's layout is fully customizable.
This is an old version of the currently published article.

 

Introduction 

First of all, I want to thank @cameron_vdb and @DavidBruant for clarifying some issues I faced in developpment.  

Lazy HTML5 Digital Clock is a Lazy Drawing & Optimized clock based on HTML5 canvas for rendering the time and JSON for Lazy Drawing. The clock's layout is fully customizable.

Live Demo.   

This article describes:     

  1. Installation  
  2. Options   
  3. Layout customization
  4. Technical Architecture 
  5. Lazy drawing (Lazy Loading Design Pattern

Supported Browsers

The Clock has been tested on all the major browsers (IE, FireFox, Chrome, Safari and Opera).


Installation

To setup the clock, add a refrence to the clock.minified.js (9 KB) file:  

<head>
    <script src="clock.minified.js" type="text/javascript"></script>
</head> 

Make sure that the DOCTYPE directive is like below:

<!DOCTYPE html> 

Reference the Javascript code below :

//
// After the page is loaded.
//
window.onload = function () {
    //
    // Initialize a new clock.
    //
    var clock = new Clock();

    //
    // Display the Current local time.
    //
    clock.display({ containerId: 'divLocal' });
}

The containerId is the only required option. This option contains the Id of the DOM object where the clock will be displayed. In the sample below, the clock is displayed in a div with the id divLocal.

By, default the width of the clock is 200px, the height is 25px and the time is displayed in hours, minutes and AM/PM mode.

The clock provides the ability to adjust its style and behavior through several options. To customize it, read the options documentation in order to get in touch with the different options. 

Options

Below the options provided by the clock:

{
    //
    // The id of the DOM object where the clock will be displayed.
    //
    includeSeconds: true,     // Optional. By default false.

    //
    //  Enables/Disables AM/PM mode.
    //
    enableAMPMMode: true,     // Optional. By default true.

    //
    //  The color of the clock content.
    //
    color: '#ddd',            // Optional. By default 'Black'.

    //
    // The width of the clock.
    //
    width: 200,               // Optional. By default 200px.

    //
    // The height of the clock.
    //
    height: 25,               // Optional. By default 25px.

    //
    // The font size of the clock content.
    //
    fontSize: 20,             // Optional. By default 30.

    //
    // The font family of the clock content.
    // 
    fontFamily: 'sans-serif', // Optional. By default 'sans-serif'.

    //
	// Time zone.
    // Possible values 'local' or 'utc'. If 'utc' is selected as 
    // the value parameter TimeZone, the UTC time will be displayed.
    //
    timeZone: 'local',        // Optional. By default 'local'.

    //
    // The number of hours to add to the current time. Is set to 0 By default.
    // Useful to display time of different time zones and cities.
    //
    addHours: 0,              // Optional. By default 0.

    //
    // The number of hours to substract from current the time. Is set to 0 By default.
    // Useful to display time of different time zones and cities.
    //
    substractHours: 0,        // Optional. By default 0.

    //
    // Indicates whether time hours, minutes, secons have be written in two digits. 
    // For example, 1:1:1 will be transformed to 01:01:01. Is set to true By default.
    //
    formatTime: true          // Optional. By default true.
}

Layout Sample

The clock can be customized through the color, the width, the height, the font size and the font family, and the DOM container.  

In this sample, I produced a custom layout by styling the container and setting a custom color.  

 Below the HTML page: 

<!DOCTYPE html>
<html>
<head>
    <title>Current Local Time</title>
    <meta name="author" content="Akram El assas" />
    <script src="clock.minified.js" type="text/javascript"></script>
    <script type="text/javascript">
        //<![CDATA[
        window.onload = function () {

            var clock = new Clock();

            clock.display({ 
                containerId: 'divLocalTime',
                includeSeconds: true,
                color: '#ddd'
            });
        };
        //]]>
    </script>
    <style>
        h1{color: #999;}
        #divLocalTime
        {
            width: 220px;
            text-align: center;
            background: #272727;
            padding: 10px;
            box-shadow: 0 0 5px rgba(17, 17, 17, 0.8);
            border-radius: 20px;
        }
    </style>
</head>
<body>
    <h1>Current local time</h1>
    <div id="divLocalTime"></div>
</body>
</html>

Architecture

This section describes the technical architecture of the clock.

Strict mode

 The clock uses the strict mode. The strict mode is a new functionality in ECMAScript 5. This mode allows to place a program, or a function, in a 'strict' operating context. This strict operating context prevents certain actions from being taken and throws more exceptions.

This mode ensure that the script code is clean.

The Clock object  

The clock object provides one option: 

//
// Initialize a new clock.
//
var clock = new Clock({
    //
    //  Enables verborse mode, the code execution is traced 
    //  in the Javascript Browser console.
    //
    enableLogging: true //By default false
});

This option allows the developer to trace the execution of the script code in the Browser console. Very usefull for debugging.

This option is by default disabled. Leave this option to false in production environments.

The main function that displays the current time requires only one parameter containerId that contains the id of the DOM object where the clock will be displayed.

The other options are described in the Options section.

//
// Display the Current local time.
//
clock.display({ containerId: 'divLocalTime' }); 

Once the the display function is called, the clock options and parameters are initialized, Then the clock is pre-rendered and finally the the clock is rendered and the current time is displayed on the client.

During the intialization process, the options are retrived and calculated. The canvas positions, width and height are calculated. 

During the pre-rendering process the hours canvas, minutes canvas, am/pm and seconds canvases eventually are created. Once created their 2D context is drawn lazily.

The schema below describes the main processing. 

 

Lazy Drawing with JSON  

In order to optimize the rendering (2D contexts  are drawn only when necessary), the previous time is persisted in a hidden field. The previous time is stored as JSON Data. JSON.stringify is used to serialize a Javascript object into JSON. JSON.parse is used for deserialization.

The schema below describes the main processing. 

 

Below the main rendering function.

//  
// render
// Retrieves the current time and calculate it
// with the specified timeZone, addHours and 
// substractHours parameters, and draws it
// lazily!
//
function render() {
    try {

        var now = new Date();
        var hours = undefined;
        var minutes = undefined;
        var seconds = undefined;
        var ampm = undefined;

        // Calculate the hours by timeZone
        switch (timeZone) {
            case enumTimeZoneLocal:
                hours = now.getHours();
                minutes = now.getMinutes();
                if (includeSeconds) seconds = now.getSeconds();
                break;
            case enumTimeZoneUtc:
                hours = now.getUTCHours();
                minutes = now.getUTCMinutes();
                if (includeSeconds) seconds = now.getUTCSeconds();
                break;
        }

        if (hours != undefined & minutes != undefined) {

            // addHours, substractHours
            hours += hoursToAdd;
            hours -= hoursToSubstract;

            //
            // AM/PM
            //
            if (isAmpmModeEnabled) ampm = getAmpmText(hours);

            if (hours >= 24) { // Adjust hours and AM/PM If necessary
                hours -= 24;
                if (isAmpmModeEnabled) ampm = 'AM';
            }

            if (hours <= 0) { // Adjust hours and AM/PM If necessary
                hours += 24;
                if (isAmpmModeEnabled) ampm = 'PM';
            }

            if (isAmpmModeEnabled && hours > 12) hours -= 12; // Adjust hours If isAmpmModeEnabled

            // Draw the canvases lazily!
            drawCanvases(hours, minutes, seconds, ampm);

            //
            // Push the current time in memory in order to see if the canvases have to be re-drawn or not 
            // With the Lazy loading design pattern, The performance is strongly increased. 
            // If you were a very lazy canvas, what would you not like to do in this clock? 
            // If I were a very lazy canvas, I would not appreciate drawing the same context
            // again and again. So for example, If you refresh all the digit canvas every 1 second to get the time
            // with seconds included, the minutes canvas will be redrawn 59 times and the hours canvas 60 * 59 = 3540 times.
            // But, If I am a very lazy canvas, I'll cache in memory the previous time beeing displayed 
            // and then check If the seconds canvas have to be updated, If the minutes canvas have
            // to be updated, If the hours canvas have to be updated, and If the ampm canavas have to be updated.
            // In conclusion, every canvas will be redrawn only If its content changes.
            //
            var time = includeSeconds
                ? {
                    hours: hours,
                    minutes: minutes,
                    seconds: seconds
                }
                : {
                    hours: hours,
                    minutes: minutes
                };

            var jsonTime = JSON.stringify(time);
            memory.pushJSONData(jsonTime);
        } else {
            logError(containerId, '[doWork] ' + ' minutes and hours are undefined.');
        }

    } catch (e) {
        logError(containerId, '[doWork] ' + e);
    }
} 

If you were a very lazy canvas, what would you not like to do in this clock? If I were a very lazy canvas, I would not appreciate drawing the same 2D context again and again. So for example, If you refresh all the digit canvas every 1 second to get the time with seconds included, the minutes canvas will be redrawn 59 times and the hours canvas 60 * 59 = 3540 times. But, If I am a very lazy canvas, I'll cache in memory the previous time beeing displayed and then check If the seconds canvas have to be updated, If the minutes canvas have to be updated, If the hours canvas have to be updated, and If the ampm canavas have to be updated.

In conclusion, every canvas will be redrawn only If its content changes.  

The schema below describes the main processing.

 

Below the main function that draws canvases.

//  
// drawCanvases
// Draws the hours, minutes and seconds in 
// their corresponding canvas lazily!
//
function drawCanvases(hours, minutes, seconds, ampm) {
    try {

        //
        // Get the previous time from memory.
        //
        var previousHours = undefined;
        var previousMinutes = undefined;
        var previousSeconds = undefined;

        if (memory && memory.pullJSONData) {
            var previousJSonTime = memory.pullJSONData();
            if (previousJSonTime) {
                var previousTime = JSON.parse(previousJSonTime);
                previousHours = previousTime.hours;
                previousMinutes = previousTime.minutes;
                previousSeconds = previousTime.seconds;
            }
        }

        //
        // Draw the canvases lazily!
        //

        if (hoursCanvas) {
            if ((previousHours && previousHours < hours) || (hoursCanvas && previousHours == undefined)) {
                drawCanvas(hoursCanvas, xcanvas, ycanvas, formatDigits(hours), true);
                logSuccess(containerId, '[drawCanvases] [hours] previous=' + previousHours + ' current=' + hours);
                logSuccess(containerId, '[drawCanvases] [hours] canvas drawn');
            }

            if (minutesCanvas) {
                if ((previousMinutes && previousMinutes < minutes) || (minutesCanvas && previousMinutes == undefined)) {
                    drawCanvas(minutesCanvas, xcanvas, ycanvas, formatDigits(minutes), true);
                    logSuccess(containerId, '[drawCanvases] [minutes] previous=' + previousMinutes + ' current=' + minutes);
                    logSuccess(containerId, '[drawCanvases] [minutes] canvas drawn');
                }

                if (ampmCanvas) {
                    var previsouAmpmText;
                    if (previousHours) previsouAmpmText = getAmpmText(previousHours);
                    if (previsouAmpmText != ampm) {
                        drawCanvas(ampmCanvas, xcanvas - 6, ycanvas, ampm, true);
                        logSuccess(containerId, '[drawCanvases] [ampm] previous=' + previsouAmpmText + ' current=' + ampm);
                        logSuccess(containerId, '[drawCanvases] [ampm] canvas drawn');
                    }
                }

                if ((secondsCanvas && previousSeconds && previousSeconds < seconds) || (secondsCanvas && previousSeconds == undefined)) {
                    drawCanvas(secondsCanvas, xcanvas, ycanvas, formatDigits(seconds), true);
                    logSuccess(containerId, '[drawCanvases] [seconds] previous=' + previousSeconds + ' current=' + seconds);
                    logSuccess(containerId, '[drawCanvases] [seconds] canvas drawn');
                }
            }
        }
    } catch (e) {
        logError(containerId, '[drawCanvases] ' + e);
    }
}

To draw the current time in the 2D context. I've used fillText. Below the main function that draws canvases.

//  
// drawCanvas
// Draws the specified text in the canvas
// at the specified x and y coordinates.
// The clea option clears the canvas before
// drawing.
//
function drawCanvas(canvas, x, y, text, clear) {
    try {
        if (clear == true) canvas.width = canvas.width;
        var canvasContext = canvas.getContext('2d');
        canvasContext.fillStyle = color;
        canvasContext.font = fontSize + 'px' + ' ' + fontFamily;
        canvasContext.fillText(text, x, y);
    } catch (e) {
        logError(containerId, '[drawCanvas] ' + e);
    }
}

The advantage of using canvas is that we have all the functions of the 2D context available so developers can customize the drawing in order to respond to their flavor and needs in terms of visual effects and layout.

setInterval is used to render lazily the canvases with the current time.  If the seconds are not displayed the timer interval is equal to 60000 milliseconds. Otherwise, It is equal to 1000 milliseconds.

//
// timerInterval
// The interval of the timer is in milliseconds.
// The default value is 1 minute = 60 secpnds = 6*1000 milliseconds = 60000 milliseconds.
// If the seconds are selected, the value changes to 1 second = 1000 milliseconds.
//
var timerInterval = includeSeconds ? 1000 : 60000;

History

14 Apr 2012 16:19 PM Published the article

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

About the Author


Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 14 Apr 2012
Article Copyright 2012 by Akram El Assas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid