Archive for February, 2012

February 28, 2012

Creating an HTML5 Timer, part 2

Last week, we created a generic timer using canvas and the setInterval function. This week, we will add some audio elements and stylize the application using CSS3.  This timer is going to have an optional ticking sound for each second that elapses and an alarm sound when it has expired.

Adding sounds in HTML5 is done by defining elements with the <audio> tag. We’ll need to define a couple of them for our sounds without the controls attribute.

 

<audio id=”clockTick” preload=”auto”>

<source src=”audio/tick.mp3″ type=”audio/mpeg” />

<source src=”audio/tick.wav” type=”audio/wav” />

audio tag not supported.

</audio>

<audio id=”alarm” preload=”auto”>

<source src=”audio/alarm.mp3″ type=”audio/mpeg” />

<source src=”audio/alarm.wav” type=”audio/wav” />

</audio>

 

Next we’ll add a helper function to play the sounds.

 

function playSoundById(elementId) {

document.getElementById(elementId).play();

}

 

Since the ticking sound is optional, users will be able to turn it off with a button that toggles.

 

<div><input id=”muteButton” type=button value=”Mute” style=”width:97px; font-weight:bold” onClick=”muteClock()” ></div>

 

function muteClock() {

clockMuted = !clockMuted;

if (clockMuted == true) {

muteButton.value = “Un-Mute”;

return;

}

muteButton.value = “Mute”;

}

 

The last step for the sounds is to make calls to the helper function that we created. The ticking sound should be called from inside the setInterval function and the alarm sound will be called from inside the finish() function.

 

if (!clockMuted) {

playSoundById(‘clockTick’);

}

playSoundById(‘alarm’);

 

Last week, we built the interface using a simple html table.  This week, we have removed the table and now rely on CSS to position the elements. This gives us a bit more flexibility for the positioning of elements as well as the styling.

 

<div id=”container”>

<section id=”displaySection”>

<div class=”time”>

<input id=”elapsedTime” value=”0:00:00″ readonly=”readonly”>

</div>

<div id=”clock”>

<canvas id=”canvas” width=”100″ height=”100″>

Your browser does not support the canvas tag

</canvas>

<div class=”controls”>

<div><input name=”starter” type=button value=”Start” style=”width:97px; font-weight:bold” onClick=”startClock()” ></div>

<div><input name=”pauser” type=button value=”Pause” style=”width:97px; font-weight:bold” onClick=”pauseClock()” ></div>

<div><input name=”resetter” type=button value=”Reset” style=”width:97px; font-weight:bold” onClick=”resetClock()” ></div>

<div><input id=”muteButton” type=button value=”Mute” style=”width:97px; font-weight:bold” onClick=”muteClock()” ></div>

</div>

</div>

</section>

<section id=”settingSection”>

<div class=”settings”>

<input id=”hours” placeholder=”hh” type=”number” value=”” onkeypress=”validate(event)” />

<input id=”minutes” placeholder=”mm” type=”number” value=”” onkeypress=”validate(event)” />

<input id=”seconds” placeholder=”ss” type=”number” value=”” onkeypress=”validate(event)” />

</div>

</section>

</div>

 

And with a bit of CSS, our timer looks like the following.

You can see the CSS for our timer by viewing the source of the demo, although I would encourage you to experiment with some different styles of your own.

Advertisements
Tags:
February 21, 2012

Creating an HTML 5 Timer, part 1

This week, we will be creating a functional timer using HTML 5 timer elements. For visualization, we will use the canvas element to visualize time elapsed as well as an overlay that displays a message when the time has expired.

As a first step, we will craft the HTML for the interface. At the top of the page, we will display elapsed time followed by a canvas element the represents the elapsed time.

<input id=”elapsedTime” value=”0:00:00″ readonly=”readonly” style=”border:none; vertical-align:middle text-align:center; width:120px; font-size:32px; font-weight:bold” >

<td>

<div id=”container”>

<canvas id=”canvas” width=”100″ height=”100″>

Your browser does not support the canvas tag

</canvas>

<div id=”timesUp” style=”position:absolute; top:95px; left:12px; color:red”></div>

</div>

</td>

You will notice that the canvas and has been wrapped in a container that includes the another div. This extra div will be used for an overlay message to be displayed when the time has expired.

Next, we’ll add some input boxes so users can specify hours, minutes, and seconds as well as some buttons for starting, stopping, and resetting the timer.

<td>

<input id=”hours” type=”text” value=”” style=”width:26px” onkeypress=”validate(event)” />

<input id=”minutes” type=”text” value=”” style=”width:26px” onkeypress=”validate(event)” />

<input id=”seconds” type=”text” value=”” style=”width:26px” onkeypress=”validate(event)” />

</td>

<td>

<input name=”starter” type=button value=”Start” style=”width:97px; font-weight:bold” onClick=”startClock()” >

</td>

<td>

<input name=”pauser” type=button value=”Pause” style=”width:97px; font-weight:bold” onClick=”pauseClock()” >

</td>

<td>

<input name=”resetter” type=button value=”Reset” style=”width:97px; font-weight:bold” onClick=”resetClock()” >

</td>

In our input boxes for hours, minutes, and seconds, we want to limit the input to integers. To do this, we’ll use the validate event and regular expressions.

function validate(evt) {

var theEvent = evt || window.event;

var key = theEvent.keyCode || theEvent.which;

key = String.fromCharCode( key );

var regex = /[0-9]|\./;

if( !regex.test(key) ) {

theEvent.returnValue = false;

if(theEvent.preventDefault) theEvent.preventDefault();

}

}

Now we will need to create the script that drives the timer. When the page is loaded, we will need to initialize some variables and render our canvas element.

var interval = 1000;

var limit = 0;

var canvas, ctx, size, startAngle, wedgeSize, fillClr, elapsed, intervalId, timerElement;

var hh=0, mm=0, ss=0;

var hInput, mInput, sInput, overlay;

$(document).ready(function() {

canvas = $(“#canvas”)[0];

ctx = canvas.getContext(‘2d’);

size = Math.min(canvas.width, canvas.height);

startAngle = -Math.PI/2;

timerElement = document.getElementById(‘elapsedTime’);

hInput = document.getElementById(‘hours’);

mInput = document.getElementById(‘minutes’);

sInput = document.getElementById(‘seconds’);

overlay = document.getElementById(‘timesUp’);

initClock();

});

Notice the initClock function that is called. We don’t have that yet, so let’s create it. This function will be responsible for resetting variables as well as drawing the initial canvas element.

function initClock() {

timerElement.value = “0:00:00”;

overlay.innerHTML = “”;

elapsed = 0;

wedgeSize = (interval / limit) * Math.PI * 2;

fillClr = “#fff8dc”;

var bgClr = ctx.createLinearGradient( 0, 0, canvas.width, canvas.height );

bgClr.addColorStop( 0, “#000000” );

bgClr.addColorStop( 1, “#999999” );

fillClr = ctx.createLinearGradient( 0, 0, canvas.width, canvas.height );

fillClr.addColorStop( 0, “#AAAAAA” );

fillClr.addColorStop( 1, “#EEEEEE” );

var drawX = drawY = radius = size / 2;

var circle = canvas.getContext(“2d”);

circle.globalAlpha = 1;

circle.beginPath();

circle.arc(drawX, drawY, radius, 0, Math.PI * 2, true);

circle.fillStyle = bgClr;

circle.fill();

circle.lineWidth = 0.25;

circle.strokeStyle = “#000000”;

circle.stroke();

}

At this point, all that is left to do is to plug in the buttons that we defined earlier. So let’s start with getting the timer running. This function will parse the user input for hours, minutes, and seconds and kick off the timer using HTML5’s setInterval function. The setInterval function will repeatedly be called until it is cleared, so we can take advantage of this to advance our visual representations.

function startClock() {

limit = (parseInt(hInput.value == “” ? “0” : hInput.value) * 3600)

+ (parseInt(mInput.value == “” ? “0” : mInput.value) * 60)

+ parseInt(sInput.value == “” ? “0” : sInput.value);

limit *= 1000;

intervalId = setInterval(function () {

elapsed = elapsed + interval;

wedgeSize = (elapsed / limit) * Math.PI * 2;

updateTime();

var drawX = drawY = radius = size / 2;

var endAngle = startAngle + wedgeSize;

var wedge = canvas.getContext(“2d”);

wedge.beginPath();

wedge.moveTo(drawX, drawY);

wedge.arc(drawX, drawY, radius, startAngle, endAngle, false);

wedge.closePath();

wedge.fillStyle = fillClr;

wedge.fill();

wedge.lineWidth = 0.25;

wedge.strokeStyle = “#eeeeee”;

wedge.stroke();

if (elapsed >= limit) {

clearInterval(intervalId);

finish();

}

}, interval);

}

You should notice a call to an updateTime() function. We’ll need to create this function as it is responsible for the text display of elapsed time.

function updateTime() {

var s,m;

ss += 1;

s = ss < 10 ? ‘0’ + ss : ss;

if (ss > 59) {

ss = 0;

s = ’00’;

mm += 1;

}

m = mm < 10 ? ‘0’ + mm : mm;

if (mm > 59) {

mm = 0;

m = ’00’;

hh += 1;

}

timerElement.value = hh + ‘:’ + m + ‘:’ + s;

}

Once the elapsed time has reached the limit, clearInterval () is called as well as a finish().  The finish() function is something we will need to create. Its purpose for now is to display some text in the overlay.

function finish(){

overlay.innerHTML = “Time is up!”;

}

Since we want our users to be able to pause the timer, we can attach the clearInterval() function to the Pause button.

function pauseClock() {

clearInterval(intervalId);

}

Finally, we will need to create a function for the Reset button. This function will stop the clock, zero out the elapsed time, and re-initialize the canvas.

function resetClock() {

pauseClock();

elapsed = hh = mm = ss = 0;

initClock();

}

You can see the demo in action here. Next week we will add a bit a polish to the interface as well as add some audio indicators for interval updates and an alarm when the time limit is hit.

February 14, 2012

Multiple View Models with Knockout

In a recent project, I was required to create a form for user input in a web page. Each section of the form is distinct and serves a unique purpose, so I chose to use a tabbed layout . The order in which the forms are completed was not a strict requirement, so some visual indication of which sections were completed was also a requirement.

I decided to use Knockout js for the data binding to take advantage its easy data bindings as well as the ko.computed method to determine which sections are complete.  While creating a design for this, it became apparent that each section (tab) should be its own view model. The problem was that there really isn’t any documentation on how to create multiple view models on a single page. There is an obscure reference deep in the Knockout js documentation that does not clearly explain how to do it.

After a bit of googling, I could not find any clear instructions on how to solve the problem; only some single line answers. After getting small hints from several google search results and some trial and error, I was able to come up with a clean solution.

The solution is actually quite simple, but I thought I would share it with since having a clear example would have saved me a lot of time.

The first thing to do is to create your sections on the page with a unique id.

<section class="tab" id="firstSection">
	<h1>First Section</h1>
	<p>First Name: <strong data-bind="text: name"></strong></p>
	<p>First name: <input data-bind="value: name" /></p>

</section>

<section class="tab" id="secondSection">
	<h1>Second Section</h1>
	<p>Second Name: <strong data-bind="text: name"></strong></p>
	<p>Second name: <input data-bind="value: name" /></p>	
</section>

One thing you may notice about this example is that it is using the same variable name called “name” in each section. This is one of the benefits of using multiple view models since those variables are only accessible in the scope of the view model.

Second, you will need to define your view models. There is nothing special about how these are defined for this particular solution.

function firstViewModel() {
	var self = this;
	self.name = ko.observable("Enter Name");			
};

function secondViewModel() {
	var self = this;
	self.name = ko.observable("Enter Name");	
};

Lastly, you will need to apply the bindings for the view models and assign them to the previously defined sections.

ko.applyBindings(new firstViewModel, $("#firstSection")[0]);
ko.applyBindings(new secondViewModel, $("#secondSection")[0]);

Take note of the second parameter passed to the applyBindings method as it does the magic of isolating the view model to a specific element on the page.  I am not sure why this isn’t clearly documented anywhere, but hopefully this post proves useful to anyone that is looking to add support for multiple view models on a single page.

A working demo can be found here