Finishing Touches

After finishing the previous section of this tutorial, you should have a fully functional simulation of Newton’s Cannon. If it’s good enough for you, you can now put this simulation aside and move on to another HTML5 project. On the other hand, if you’re a perfectionist like me, you might first want to refine the Newton’s Cannon simulation in a few ways.

Each of the following refinements can be made independently, so feel free to skip any that don’t interest you.

Trails

The original Newton’s Cannon illustration shows the paths of the projectiles, so perhaps yours should too.

The best way to add “trails” to this simulation is to draw them on a separate canvas, sandwiched in between the existing canvas and the underlying image. You can do this with just four additional lines of code:

  1. A line of HTML, in between your existing img and canvas elements, to add the second canvas. Give it id="trailCanvas", with the same dimensions as the others and position:absolute.
  2. A declaration/initialization for a JavaScript variable trailCanvas, otherwise identical to the one for theCanvas.
  3. Similarly, a declaration/initialization for the new graphics context (call it trailContext), analogous to theContext.
  4. Finally, in your drawProjectile function, a line to draw a small dot at the projectile’s current location. This is easiest if you actually make the dot a rectangle, because there’s a one-line convenience function for drawing rectangles:
    trailContext.fillRect(pixelX-0.5, pixelY-0.5, 1, 1);
    The first two parameters are the coordinates of the rectangle’s upper-left corner, so I’ve subtracted 0.5 to center it precisely; the third and fourth parameters are the rectangle’s width and height.

The new canvas’s fillStyle defaults to black, but feel free to change this to a different color if you prefer.

When you test these changes, you should find that the dots are close enough together to form a continuous curve. (If they weren’t so close, and you still wanted a continuous curve, you could use the moveTo and lineTo functions to draw lines. This would also require a couple of new global variables to store the projectile’s previous location, and a bit of code to ensure that you don’t connect the end of one path to the beginning of the next.)

Notice that the canvas can accumulate an unlimited number of trails, with thousands upon thousands of individual dots, with absolutely no performance penalty. This is one advantage of immediate-mode graphics over retained-mode graphics.

It’s also a nice touch to add a button to clear the trails and start over. Add a line of HTML code for this new button, analogous to that for the “Fire!” button. Set the onclick attribute to call a new function called clearTrails, and have this function call trailContext.clearRect in a way analogous to the first line of your drawProjectile function.

3-D shading

Newton’s illustrator used shading to give the planet a three-dimensional appearance. You can do the same for your projectile by filling it with a radial gradient instead of a solid color. In your drawProjectile function, just replace the line that sets fillStyle with the following four statements:

var theGradient = theContext.createRadialGradient(
                  pixelX-1, pixelY-2, 1, pixelX, pixelY, 5);
theGradient.addColorStop(0, "#ffd0d0");
theGradient.addColorStop(1, "#ff0000");
theContext.fillStyle = theGradient;

The parameters of the createRadialGradient function define two circles, in terms of their center locations and radii (here 1 and 5, respectively). The addColorStop functions then specify the colors on (and beyond) these circles, and the gradient automatically interpolates between them. Try changing the gradient metrics and colors until you’re happy with the appearance.

Bigger buttons

Choosing optimum sizes for things is tricky in a web app, because you don’t know the user’s screen size. There are several reasons, though, why you might want to make buttons bigger than their default sizes. A quick-and-dirty way to enlarge them is to specify a larger font via the CSS font-size property. But I’ve found that styling buttons is tricky, producing inconsistent results in different browsers.

A robust, but cumbersome, alternative is to avoid buttons entirely and instead just use links. The HTML code for your Fire! button would then be:

<a class="customButton" href="javascript:void(0)" 
    onclick="fireProjectile();" ontouchstart="">Fire!</a>

Try this out and check that the “button” still works. I don’t actually understand the reason for javascript:void(0), but seemingly knowledgable people recommend it. The empty ontouchstart attribute is a detail that improves the behavior on mobile devices.

The class attribute is a convenient way of applying the same styling to multiple elements. To make it work, you define the class inside a style element in the head portion of your source file. After much fiddling, I’ve settled on the following styling for my custom buttons:

<style>
  .customButton {  /* style a link as a push-button */
    display: inline-block; 
    width: 60px; 
    height: 24px; 
    line-height: 24px; 
    font-size: 15px; 
    font-family: sans-serif; 
    text-align: center;
    color: black; 
    background: -webkit-linear-gradient(white,#eeeeee,#eeeeee,#e0e0e0);
    background: linear-gradient(white,#eeeeee,#eeeeee,#e0e0e0);
    text-decoration: none; 
    border: 1px solid gray; 
    border-radius: 5px;
    -webkit-user-select: none;
    -moz-user-select: -moz-none;
    -ms-user-select: none;
    user-select: none;
    cursor: pointer;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
  }
  .customButton:active {
    background: -webkit-linear-gradient(#909090,#808080,#808080,#707070);
    background: linear-gradient(#909090,#808080,#808080,#707070);
  }
</style>

Yes, it’s cumbersome, and I don’t claim that it’s perfect. I hope you can guess what most of these CSS properties do. The gradients use various gray levels to give the buttons a nice 3-D appearance. (You can, of course, use brighter colors if you don’t think they’ll be too distracting.) Some of the property names are nonstandard, specific to particular browsers or groups of browsers (Webkit, Mozilla, and Microsoft). The dot before customButton indicates that we’re defining a class, and the curly braces enclose all the property settings that belong to that class. The active pseudo-class changes the button’s color while it is being pressed.

Styling the slider

Slider controls look a little different in each different browser—with the exception of Internet Explorer, in which they look a lot different. In IE the default width is much larger, and the default “padding”, or extra space, above and below the slider is absurdly large. IE also puts ugly tick marks along the slider’s track, and puts a pop-up numerical readout, rounded to the nearest integer, above the slider while you’re adjusting it.

Here’s the styling that I use to fix all these issues (formatted for use inside the style element in your page’s head):

input[type="range"] {
  width: 140px;
  padding: 0px;
}
input[type="range"]::-ms-tooltip {
  display: none;       /* hide readout in IE */
}
input[type="range"]::-ms-track {
  color: transparent;  /* hide tick marks in IE */
}	

Even with these changes, sliders will look very different in IE than in other browsers. But the remaining differences are primarily a matter of taste, and there’s something to be said for keeping the appearance consistent within each browser.

Fixed-width speed readout

You may have noticed that if you reduce the launch speed below 1000 m/s, all the GUI controls shift as they are re-centered to accommodate the loss of a digit in the speed readout. If you find this behavior annoying, you can easily fix it with a bit of styling:

<span id="speedReadout" style="display:inline-block; 
                  width:2.3em; text-align:right;">

Specifying the width in em units (each equal to the font size in pixels) is more robust than using pixel units if you later decide to change the font size. But this fix is still a bit of a kludge, because I determined the optimum width by trial and error and the number is somewhat font-dependent.

If you now view the page on a smartphone, however, you’ll see that the readout looks funny because the number is in a smaller font than the text around it. That’s because smartphones automatically enlarge font sizes for readability when a “block” (such as a paragraph or a div) is wider than a certain amount (typically 320 pixels). Giving the readout a fixed width required making it its own block, so now it doesn’t get enlarged. The best fix, at least for the most common mobile browsers, is probably to turn off the automatic enlargement by inserting

-webkit-text-size-adjust:100%;

into the style of the enclosing div.

Special characters

As a final tweak to the appearance of the line of GUI controls, you can insert a little extra space to separate logically distinct elements. One way to do this is by adjusting the left and right margins, but a slightly easier method is to insert two or three “non-breaking space” characters,

&nbsp;

into the HTML just after the Fire! button (and two or three more just before the Clear button, if you’ve implemented that).

Another nice aesthetic touch is to change the straight typewriter-style apostrophe in Newton's to a pretty typographer’s apostrophe (or right single quote):

&rsquo;

You can use similar syntax in your HTML to insert a wide variety of special characters, inluding math symbols, Greek letters, and accented letters. Each code begins with an ampersand and ends with a semicolon, with a unique few-letter sequence in between. Several of the most useful are listed on the accompanying HTML and styling reference sheet, which also provides a link to a complete list.

More tweaks for mobile

On touch screen devices, touching an element on a web page can sometimes make the browser think you want to select it for copying. That makes sense for text, but not for most GUI controls. I’ve found this behavior particularly annoying for sliders, so I generally put the following CSS into the <style> element in my document’s header:

input {
  -webkit-user-select: none;
  -moz-user-select: -moz-none;
  -ms-user-select: none;
  user-select: none;
}

Another issue with mobile devices is that they need to know or assume a value for the full width of your web page in pixels—and on iOS (at least), this number defaults to an annoyingly large value of 980. (I try to keep the content of my pages considerably narrower, so they won’t monopolize my laptop screen or exceed the width of an 800-pixel projector.) Fortunately, you can change this default by putting another meta tag into your page’s header:

<meta name="viewport" content="width=640">

As long as users are holding their devices in portrait orientation, the optimum setting for the content width is just a little more than the width of your page’s body (or widest block). The down-side is that this makes your page harder to use in landscape orientation, because users probably won’t be able to zoom out far enough to see all the important parts at once. Try it, in any case, and decide what setting best suits your layout and the uses you have in mind.

Sound effects

I normally subscribe to the philosophy that computers should be seen and not heard. But the Java version of Newton’s Cannon that inspired this tutorial has some cute sound effects that are kinda fun, at least for a little while. So I’ve tried to implement sound effects in my HTML5 version of Newton’s Cannon, and they work fine on traditional computers, but not on my iOS devices. There should exist a fix for the latter, but I haven’t yet found the time to implement and test it. If and when I do, I’ll describe the solution here.

Next Steps

Back to index

Copyright © 2014, Daniel V. Schroeder