Monday, February 6, 2012

Trigonometry for Web Developers, Part Three

Recap of Part Two
In part two of this series, we learned about the use of the sine and cosine functions and how they can be used to transform a set of polar coordinates (angle and distance) into cartesian (x,y) coordinates.   We  then used them along with an HTML canvas object and the associated translate() and rotate() methods to draw tick marks and labels on our canvas object.    In this final installment, we'll add a needle to our meter and learn a bit about how to efficiently animate it to display rapidly changing values.

Drawing the Needle
Our analog meter example wouldn't be complete without a needle to indicate the current value.    Just like on a real meter, the needle on our example pivots on one end around a point centered along the lower edge of the canvas while the other end points at the value to be displayed.    Conceptually, this is just like drawing another tick line as we did in the previous example: we will compute the angle and then use it, along with a predetermined length, to find the polar coordinates for one end of the line.   We will then draw the line using those coordinates along with the center bottom of the canvas.

The Javascript code required to draw the needle is straightforward.
// Determine the angle of the needle
needleAngle = startAngle + a_value * ((endAngle - startAngle) / 10.0);

// Draw the needle
ctx.clearRect(0, 0, cvs.width, cvs.height);
ctx.beginPath();
ctx.moveTo( centerX, bottomY );
ctx.lineTo( centerX + Math.cos(needleAngle) * (radius * 0.80), 
            bottomY + Math.sin(needleAngle) * (radius * 0.80));
ctx.stroke();
The angle of the needle is first determined by finding the polar distance between the starting and ending angles.   That distance is then divided by ten (the range of our scale), then multiplied by the value.    This result would be all that was needed if the zero value of the needle was aligned with 0 degrees on the graphics context, but in order to match it to the meter's face, the starting angle must be added.
Similar to drawing the tick marks, the drawing cursor is moved to one end of the needle's position and line is then drawn from there to a coordinate determined through the use of sine and cosine functions.

Adding a Layer
While it is certainly possible to draw the needle on the same HTML canvas as the tick marks, labels, etc. it is not very efficient to do so.   Consider that the needle may need to move frequently, thus needing to be redrawn, but the face of the meter is constant.   It would be very inefficient to re-draw the entire meter every time the value to be displayed changes, so we'll use two canvas objects: one for the labels and other items which do not change, and another for the needle.   We will then place them "on top" of each other, with the canvas for the needle in front.   This allows the animation to be much more efficient:  we can draw the face of the meter only once, and draw only the needle when the value is changed. 
<div style="width:500px; height:250px;">
  <canvas id="meter" width="500" height="250" 
          style="position:absolute; left:0; top:0; z-index:0"> 
  </canvas>
  <canvas id="needle" width="500" height="250" 
          style="position:absolute; left:0; top:0; z-index:1"> 
  </canvas>
</div>
The HTML required to set up the layering is shown above.     Notice that each canvas object is given an absolute position within it's containing DIV object.   This is what causes the canvases to appear to be "stacked".   Also, note the z-index value in the style attribute; this is what determines which canvas is "in front".    HTML objects with increasing z-order values will appear "closer" to the user than those with smaller values.  

The full HTML source which demonstrates drawing the needle into a separate canvas can be found here (FF, Chrome, and IE9).


Going Further
As you have seen, combining trigonometry, the HTML canvas object, and some Javascript can yield some neat results.   While it is beyond the scope of this series of articles, the HTML canvas object has some powerful drawing features.   Using the trigonometry concepts presented so far along with not-a-lot-more canvas object methods, you can produce impressive results with relatively little effort - click on the clock for the full-size, animated version (FF, Chrome, and IE9 only) :

This example uses the same techniques described previously, and adds some visual enhancements through the use of gradient fills, shadowing, and line style features of the HTML canvas object.    Like our sample meter, two separate layers are used: one for the clock face, numbers, etc. and a second canvas that only contains the clock hands.     No images were used for this example, so even without minification or other optimization techniques, the file size comes in at around 8KiB which provides for excellent performance even on slow links.

Wrapping It All Up
There is much more that could be done with this relatively simple example, but we have now covered the basics needed to handle angular data and use trigonometry to transform it to the x,y coordinate system used by the HTML canvas object.    If nothing else, remember this :
  • Math functions think in radians.   Convert degrees to radians by multiplying by π/180.0 and convert radians to degrees by dividing by π/180.0.   
  • cosine(angle) returns an X-coordinate, normalized to values between -1.0 and +1.0.   Multiply this value by the radius and add/subtract as needed to move the center of the circle away from X-coordinate 0.
  • sine(angle) returns the Y-coordinate, normalized to values between -1.0 and +1.0.   Multiply this value by the radius and add/subtract as needed to move the center of the circle away from Y-coordinate 0.
A little trigonometry added to an HTML canvas is an easy way to build an experience for your site that is fresh and different from the majority of rectangular, boxy sites which exist today.     What can you build with a little trig?


No comments:

Post a Comment