Skip to content
April 20, 2012 / geeksretreat

Making a thermometer using HTML5′s Canvas

Following on from my  Making a speedometer using HTML5′s Canvas post I decided to attempt another project.  This time one’s intention was to create a thermometer using the Canvas implementation.  However, I decided to take a slightly different approach.  In my first post I exclusively used the rendering context of the Canvas to draw each part of the speedometer.  For this project one’s intention was to use a static image for the instrument ‘s body which consists of the wooden frame, measurement marks and the glass tube.  Of course, one could draw the instrument’s body by using a combination of the various shape functions provided with the Canvas implementation; however, the only moving part of a thermometer is the liquid within the glass tube; thus, the body can, and arguably should be, loaded from a static image.
In addition to the Canvas feature, I have also implemented HTML5′s slider control.  The slider control is not supported by all major browsers yet; but, before long it will be!  As of writing this post I think it’s only supported in Chrome and Safari.  The control when implemented should look as follows:

HTML5's slider

Hopefully, you can already see one’s intention.  By linking the scroll control with the thermometer we can dynamically draw the liquid within the glass tube. A working example can be viewed here

The HTML Source

The following code is stripped to the minimum in order to draw the speedometer above.

<html lang="en">
	<head>
		<meta charset='utf-8' />
		<title>Canvas Thermometer </title>
		<script src='thermometer.js'></script>
	</head>
	<body onload='draw();'>
		<div>
			<canvas id='thermometer' width='200' height='900'></canvas>
			<div style='position: absolute; top: 200px; left: 250px;'>
				<form id='drawThermometer'>
					<input type="text" id="temp" name="temp" value="-30" maxlength="3"/>
					<br>
					<input id="defaultSlider" type="range" min="-30" max="50" onchange="setTempAndDraw();" />
					<br>
					<input type="button" value="Draw" onclick="draw();">
				</form>
			</div>
		</div>
	</body>
</html>
The HTML is fairly straight forward. The items of interest are the canvas, the ‘onload’ event for the body and the script tag. For those who are not familiar with HTML, let me explain how the canvas turns into the thermometer. When any HTML page loads the browser will run the JavaScript code attached to the onload event of the body tag e.g.
<body onload='draw();'>
In this example the JavaScript function called ‘draw’ will be called. The next logical question is what and where is this function? In this case the draw function is defined in the script included in the header:
<script type="text/javascript" src="thermometer.js"></script>
The browser will load any JavaScript code included in a script tag prior to displaying the page to the user. Therefore, the content of my script “thermometer.js” will be loaded and thus the function called ‘draw’ will be available. If you were to look inside the aforementioned script you will be able to see the code e.g.
function draw()
{
	/* Main entry point got the thermometer Canvas example
	 * Simply grabs a handle to the canvas element and
	 * check the conect (Canvas support). 
	 */
	
	var canvas = document.getElementById('thermometer');

	// Create the image resource
	img = new Image();

	// Canvas supported?
	if(canvas.getContext)
	{
		// Setup the onload event
		img.onload = imgOnLoaded;
		
		// Load the image
		img.src = 'thermometer-template.png';
	}
	else
	{
		alert("Canvas not supported!");
	}
}
The JavaScript above is concise and pretty self-explanatory. The script creates a handle to the element with the id of ‘thermometer’ which happens to be the canvas element.
	<canvas id='thermometer' width='200' height='900'></canvas>
It also creates a new image resource and sets-up an event handler for the image. This event handler is significant because it is the function that draws the fluid level within the glass tube. The event instructs the JavaScript engine to call the function ‘imgOnLoaded’ once the image’s source has been loaded. Therefore the following line is also equally significant as the event will be executed after the image has been loaded:
	img.src = 'thermometer-template.png';
That leads us onto the ‘imgOnLoaded’ function:
function imgOnLoaded()
{		
	/* Simply grabs a handle to the canvas element and
	 * check the context (Canvas support). 
	 */
	
	var canvas = document.getElementById('thermometer');
	
	// Canvas supported?
	if(canvas.getContext)
	{
		var ctx = canvas.getContext('2d');
		var iTemp = getTemperature();
		var iRatio = getRatio(iTemp);
		var iTempToYCoord = convertTempToScreenCoord(iRatio, iTemp);

		// Draw the loaded image onto the canvas
		ctx.drawImage(img, 0, 0);
		
		// Draw the liquid level
		drawLiquid(ctx, iTempToYCoord);

	}
	else
	{
		alert("Canvas not supported!");
	}		
}
The draw function first obtains a handle to the canvas element and then checks its context. This is a simple check to ensure the browser rendering the page supports the canvas element. If the canvas context is obtained the function then reads the temperature input by the user. The JavaScript for this is within the ‘getTemperature’ function e.g.
function getTemperature()
{
	var temperatureControl = document.getElementById('temp');
	var iTemp = 0;
	
	// Ensure the temperature value is a number
	if(temperatureControl != null)
	{
		iTemp = temp.value * 1.0;
	}
	
	// Sanity checks
	if(iTemp > 50)
		iTemp = 50;
	if(iTemp < -30)
		iTemp = -30;
				
	return iTemp;
}
The function is self explanatory. The value input is read and validated. The validated result is then returned to the calling interface. Once the temperature has been obtained the function determines the ratio between 1 degree and the pixel distance between each tick on the Celsius ruler e.g.
function getRatio(iTemp)
{
	/* The image is not in proportion this the gauge to pixel 
	 * ratio need slight adjustment
	 */
	
	var iRatio;

	if(iTemp > 0)
		iRatio = 7.1;
	if(iTemp <= 0)
		iRatio = 6.9;
	else if(iTemp < -20)
		iRatio = 6.77;

	return iRatio;
}

As the comment-block states, the ratio needs adjusting due to the slightly stretched background image. That said, the principle of the ratio is simple. Now the main draw function has the ratio and the temperature. It then passes these to the function that calculates the screen coordinates required to draw the red liquid with the glass tube:

function convertTempToScreenCoord(iRatio, iTemp)
{
	/* Algorithm to convert the temperature to the correct x screen coord.
	 * Odd, but works!
	 */
	var iMAX_TEMP = 50;
	var iSTART_Y_POS = 147;
	
	return iSTART_Y_POS + (iRatio * (iMAX_TEMP - iTemp));
}

And finally, the main function draws the image onto the canvas and then calls the ‘drawLiquid’ function e.g.

	// Draw the loaded image onto the canvas
	ctx.drawImage(img, 0, 0);
		
	// Draw the liquid level
	drawLiquid(ctx, iTempToYCoord);

The ‘drawliquid’ function simply renders a red rectangle which represents the liquid in the glass tube. The coordinates to plot the rectangle are mostly fixed with the Y coordinates based on the aforementioned algorithm e.g.

function drawLiquid(ctx, iTempToYCoord)
{
	/* Draw red rectangle to represent the fluid in the glass tube
	 * Coordinates you Y and are fixed!
	 * TODO: Calculare Y coord base on image X,Y
	 */
	
	var iX_POS = 111;
	var iY_POS = 7;
	var iY_OFFSET = 686;
	var iWIDTH = 35;
	
	ctx.fillStyle = "rgb(200,0,0)";
	
	// Draw rectangle from -30 to iTempToYCoord
	ctx.fillRect(iX_POS, iTempToYCoord, iY_POS, iY_OFFSET - iTempToYCoord);
	
	// Draw rectangle from botton to -30
	ctx.fillRect(iX_POS, iY_OFFSET, iY_POS, iWIDTH);

	ctx.stroke();	
}

That almost concludes the post. The last functionality of interest is how does the scroll control interact with the canvas. Well it has a function bound to its ‘onchange’ event e.g.

<input id="defaultSlider" type="range" min="-30" max="50" onchange="setTempAndDraw();" />

Meaning when the user drags the control around the JavaScript function named ‘setTempAndDraw’ is executed. Let us take a look at this function:

function setTempAndDraw()
{
	/* Function called when user clicks the draw button
	 */
	
	var temp = document.getElementById('temp');
	var slider = document.getElementById('defaultSlider');

	if(temp != null && slider != null)
	{
		temp.value = slider.value;
		draw();
	}
}

With any luck you should be able to work out how this function links in with the JavaScript detailed above. It basically grabs a handle to the slider control and ascertains its value. This value is then used to set the value within the temperature input control. The function’s last action is to call the ‘draw’ function which I explained above. As the ‘draw’ function uses the value within the temperature control the newly drawn thermometer will mirror the value set by the slider.

That concludes one’s experiment with HTML5′s canvas. I hope this helps someone out there. All the code above may be taken and used for anything – credit not required. Although, it would be nice to know. If you can explain any of the math I have stumbled through, or have a resource I could read please do post them below. The full source code for this example and images can be obtained from my GitHub repository https://github.com/rheh/HTML5-canvas-projects
About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 159 other followers

%d bloggers like this: