Here’s what our newly trained ninjas said about our first JavaScript Master Class that we did in June:
“JavaScript Master Class worth every cent. Tons of docs, answers, insights, fun & encouragement. Highly recommended, super friendly.”
“Enjoyed it, learnt a lot and the online format worked very well.”
“Help & clarifications on the side [in the chat] = AWESOME team”
“Completely Impressed”
We have not one, but TWO Master Classes lined up in July, both are two half-days so you get to breathe (and maybe do some of the homework!) between the parts. Both the classes are in US-friendly time zones (one is optimal for ET, the other one for PT).
All you need is a browser with Flash installed (I know, I know!), and we’re streaming live to you and you can interactively question everything and ask for more information on just about anything about JavaScript (we know a lot!).
Head over to our JavaScript Master Class site to learn more and sign up (right now, we still have early bird pricing on both classes, but not for long– you save a cool $70 with the early bird pricing!).
About a month ago or so, Amy and I release a little (literally, it’s about 5k) HTML5 iPad App for looking up time zones. I don’t mean select-box wasteland like all other time zone sites (who likes select boxes anyway?!), I mean a nicely polished, touch-enabled UI that works offline, too.
The site uses no images (‘cept for the ad), no JavaScript framworks, and no external CSS, and fits quite comfortably in a few k’s of gzipped HTML.
Our first attempt ran really great on the Apple-supplied SDK iPad Simulator. But when we got our real iPads, everything seemed slow and sluggish. We went in an optimized the perceived “snappyness” when dragging the the time bar with your finger, and about doubled the rate of frames per second rendered.
And here’s what we did:
1. Images slow things down immensely– get rid of them
At first, we used a quite complex -webkit-gradient for the gradients in the “day bars” for the various cities. It turns out the this really, really slows things down in terms of rendering speed on the iPad. What -webkit-gradient really does is construct an image bitmap, and to the rendering engine it’s exactly the same as if you would supply and externally loaded image (like a PNG file). Images on Mobile Safari are notoriously slow (hope this will improve in 4.0!), and there’s basically nothing you can do about this– except not using images and gradients.
We replaced those bars with a big canvas element that sits in the background, and we redraw only those portions of the screen that undergoes changes (the different highlighting when you drag around the green bar). You can sort-of reuse -webkit-gradient when using canvas, like this:
Also a major source of slowdown. It’s best to avoid those CSS properties.
3. Hardware-acceleration is quite new… and buggy
On Safari, all you need to enable hardware-acceleration is to use the -webkit-transform CSS property (opacity also works, but see above).
There are limits to what works with hardware-acceleration, for example the number of concurrent animations is limited– there will be flickering and rendering errors if you have more then a handful of animations going on. Gently used, it makes things awesome however (can’t wait to share the new scripty2 hw-accel demos, coming next week I hope!).
4. Use touch events whenever you can
Touch events are great, because regular old onclick events only happen with a slight delay on the iPad. Opt for touch event instead– look into the source of the page for some hints on how you can provide support for non-touch-enabled browsers at the same time:
var supportsTouch = 'createTouch' in document;
element[supportsTouch ? 'ontouchmove' : 'onmousemove'] = function(event){
var pageX = event.pageX;
if (event.touches) pageX = event.touches[0].pageX;
// ...
}
5. Avoid opacity
For some reason, using the opacity CSS property sometimes interferes with hardware-accelerated rendering, so if you experience slowdowns and think you’re doing everything right, see if you have opacity set somewhere.
6. There is no silver bullet– hand-code JavaScript and CSS
Don’t rely on frameworks or what standards zealots tell you. In our case, a highly trimmed HTML page, with inline CSS, just some pure JavaScript without a framework and using the capabilities of the target platform (iPad) as much as possible allows for a lean page that loads almost instantly, caches well and works great offline. Yes, we could have used JavaScript and CSS frameworks, but sometimes less is more (and remember, you don’t need all the cross-browser heavy lifting that frameworks do for you).
// mini-pico-tiny convenience micro-framework, ymmv
function $(id){ return document.getElementById(id); }
function html(id, html){ $(id).innerHTML = html; }
function css(id, style){ $(id).style.cssText += ';'+style; }
function anim(id, transform, opacity, dur){
css(id, '-webkit-transition:-webkit-transform'+
',opacity '+(dur||0.5)+'s,'+(dur||0.5)+'s;-webkit-transform:'+
transform+';opacity:'+(1||opacity));
}
7. Use translate3d, not translate
When using -webkit-transform, be sure to use the translate3d(x,y,z) syntax, instead of using translate(x,y). For some reason, the latter is not hard-accelerated, at least not on iPhone OS 3.x (it seems to work fine on desktop Safari, tho). Thanks to Matteo Spinelli for pointing this out (check out his iScroll project for some a great code that demonstrates how to optimize performance on mobile WebKit)!
I think it’s quite exciting to develop apps with HTML5 on these devices, with almost complete desktop-like performance in most cases, and you can even distribute apps yourself, no need to go through the App Store for this.
I’d love to hear about your experiences, and quirks and tricks you may have come across.
Want to learn how to build your own AWESOME mobile HTML5 apps? Amy and I proudly present: HTML5 Mobile Pro Workshop, a half-day online workshop on September 20, 2010, on building really great mobile HTML5 apps. We’ve limited the workshop to 25 virtual seats—so hurry if you want to join!
This summer, why not brush up your JavaScript skills? Really know what this does? Or what a prototype chain is (and why you might need it)? And what about that delete operator… And how can JavaScript code be turned from a big mess into neat building blocks?
The class is online, so you can take it in in a relaxed atmosphere, like your home in your pajamas, following along as we introduce you to the real JavaScript, the language that sits there quietly hiding (and sometimes loudly complaining) behind your favorite framework.
We’re not discussing any framework specifics, but focus exclusively on the language JavaScript, so whether you use Prototype, jQuery, YUI, dojo, or server-side hotness like node.js, you’ll learn a lot.
You get full all-day interaction with Amy and me, and can ask questions and one-on-one answers, as we will have a chat room, exercises and “homework”. And of course, you get course materials in PDF/HTML/JS form beforehand.
Oh, and on top of all this you’ll also receive a free copy of our JavaScript performance ebook including the DOM Monster, our DOM performance tool, yum!
We also plan to have these online, interactive classes in other time zones, please let us know if you’re interested in that, and whether or not you’d like a full one-day course like this one, or two half-day courses better! Plus, we also plan to do online performance workshops, let us know too if you’re interested.
*excl. VAT, if you’re from outside the EU, or have a business in a EU member country you are VAT-exempt and we’ll refund the VAT, otherwise it’s 20% Austrian VAT
The second argument to parseInt is a Radix to be used for the conversion of the string into a number. A radix of 10 means, use the decimal numeral system, which is probably what you want (the decimal system is also called “base ten”).
Some of you might strike this as quite verbose, and indeed, most other programming languages basically assume base 10, and let you specifically override the radix in case you really need to use that base 13 or base 7 numeral system. You can work around the verbosity and actually have a little bit of a performance optimization at the same time by coercing the string into a number, by applying an operator that requires numerical arguments.
Here is one way to make this shorter, and avoid the call to parseInt altogether:
// unary + operator
+"08" // -> 8
Though short and concise, the unary + operator might not always be a good choice, especially if you do calculations or string concatenation. You can use a double binary negation instead:
Be aware that while the unary + operator works with floating point numbers too, applying binary operators like the double negation will also cut off the fractional part of your number:
This can be used if you quickly want to convert a number that’s provided in a string and also cut off the fractional part (equivalent to rounding down for positive numbers).
If you need to optimize iterative algorithms, these optimizations can really help bringing down code size and as a side effect avoid expensive function calls (but please never ever optimize without a reason!).
Flash games are facing stiff competition from JavaScript. You’ll see why when you follow along with this tutorial to create your first JavaScript-based game. How about a game of memory?
Naturally, we’re talking about animations here. The bar has never been higher in terms of interactivity in games. Games that don’t move are just no fun. Happily, in 2010, games built in JavaScript can be just as shiny, interactive, and fluid as games built in Flash.
All you need is a little help from a few open source tools.
For the visuals, we’ll use my Scripty2 animation framework, the successor to script.aculo.us. You’ll learn about easings, how to use and combine its built-in effects, and how to write your own. It’s easier than you think.
We’ll also use the Prototype JavaScript framework to write shorter, cleaner code, especially when managing the data structures and game logic. Like Scripty2, Prototype can change the way you think about JavaScript (and yes, we’re giving the latest and great version of Prototype a spin, which is available as Prototype 1.7 Release Candidate 1).
The best thing about learning to develop games is that the skills are useful even if you don’t want to be a game developer. Animations, easings, and the programming logic that drives them can help you make your “regular” web apps more useful and compelling. JavaScript as a technology has come so far, now it’s time for the interfaces built with it to catch up.
Our little memory game requires a playing field with 16 playing cards, a score field (1 point for each revealed pair) and a number of tries field (increments for each revealed card, regardless if a pair is found or not). It’s good style to add a DOCTYPE (we’ll use XHTML 1.0 Transitional) and define the character set as UTF-8.
For fast loading, the best practise is to include JavaScript files at the bottom of your HTML, just before the closing
tag. The tutorial demo code comes with all the files you need. Include the minified prototype.s2.min.js file, and the demo code in memory.js (which is not minified and heavily commented for easier comprehension).
3. Prepare card pair graphics
Fire up your graphics editor of choice and design 1 square card back graphic and 8 designs for the front (our memory game will use 16 cards, so it’s 8 pairs), resolution 140×140 pixels. The tutorial code comes with prefabricated graphics for this, with the scripty2 logo for the back and the numbers 1 to 8.
4. Shuffling cards
var cards = $$('div.card');
cards = cards.sortBy(Math.random);
To shuffle, first grab an array of all 16 card elements with var cards = $$('div.card') (memory.js, line 8), then use cards.sortBy(Math.random) (line 35) to randomly reorder. Array#sortBy is a Prototype JavaScript library function, to easily sort arrays. Feeding it JavaScript’s Math.random function is a handy shortcut. The expression returns a new array with the elements in random order.
In Line 154 of memory.js, the shuffle and win functions are exported to the global namespace, for easy testing (no need to play through a complete game when you just want to tweak the winning animation timings). In the actual code, 'object' refers to the argument given to the outer wrapper function, which is 'window' (line 157), and'object' is the global namespace object when JavaScript is executed in a web browser.
6. Handling the click event
function dispatchClick(event, element){
// the "element" argument contains the card that was clicked on!
// now do stuff!
}
$('field').on('click', 'div.card', dispatchClick);
There’s two ways the clicking can be handled, either be registering event handlers on each card invidually, or using a dispatcher pattern. Opting for the latter is a good choice for games, as it allows tighter control. The dispatchClick function (lines 107 to 153) is registered on the “field” DIV, and detects which card was clicked on, by using Prototype 1.7’s handy new “on” function (line 151).
7. Add game logic
if(!element.retrieve('picture_revealed')){
var id = reveal(element);
$('tries').innerHTML = (++tries);
// second card revealed, check if we have a matching card pair
if(currentId){
blocked = true;
// yes, up score and run nice animation to remove the cards
if(currentId == id){
score++;
updatescore.delay(1);
(function(){
cards.findAll(function(card){
return card.retrieve('picture_id') == id;
}).each(function(card){
card.morph('opacity:0',{
transition: 'pulse', duration: 1
});
});
blocked = false;
}).delay(0.5);
if(score==8) win.delay(1.5);
// no, hide all cards (after 1 second)
} else {
(function(){
cards.each(function(card){ hide(card); });
blocked = false;
}).delay(1);
}
currentId = null;
// first card revealed
} else {
currentId = id;
}
}
There are two distinct possible states in the game when the player clicks a card:
No card is currently revealed: reveal the card that is clicked on
One card is already revealed: reveal the card that is clicked on and compare both shown cards, if a pair is found, remove those cards and update the score. Otherwise turn both cards on their back.
If the score reaches 8, the player wins the game.
8. Score keeping
// in dispatchClick, when a pair was revealed
score++;
updatescore.delay(1);
For each revaled pair, add a score point (line 121, 122). delay is a Prototype function that delays a function call for the given time, so the updating of the HTML is done a second later.
function updatescore(){
$('score').update(score).setStyle('color:#77a638').morph('color:#aaa', 2);
}
The updatescore function sets the HTML, gives the new score a green color and then animates the color to fade to gray (line 85).
Shuffling resets the score and number of tries counts (line 23, 24).
9. Add shuffling animation
// animate cards to go to new positions
cards.inGroupsOf(4).each(function(group, x) {
group.each( function(card, y){
flip(card, 'back.png');
card.morph('opacity:1;left:'+(x*SIZE)+'px;top:'+(y*SIZE)+'px', {
duration: duration || 0.5, transition: 'easeInOutQuint'
});
});
});
In the shuffle function, the movement of cards to a new position is achieved with just a few line of JavaScript, mostly to define animation options (lines 43 to 45). One key to good use of animation is to keep the duration short, as the animation should visually indicate that shuffling takes place, but shouldn’t feel sluggish or too drawn-out.
10. Basic easings
Easings are a mathematical function describing a change in timing. By default, Scripty2 runs effects with easing, so movements accelerate at the start of the effect, and decelerate smoothly at the end of the effect. There’s several predefined easings available, some of them taken from Flash game programming. Learn more and play around over at the scripty2 documentation.
When revealing cards and hiding them again (when no matching pair was found), a “card flip” animation . To do this, two animations are chained: 1. the width of the image is gradually reduced to 0 (while keeping the height), accelerating troughout, 2. the card images is switched, and the width is expanded back to 140, decelerating.
12. Found a pair animation with advanced transitions
The transition system can be used for advanced scripting of the effects, for example for making an animation swing from or to a position, bouncing elements, pulsating or throbbing or even for random flickering. An effect often used in games is pulsating or blinking to grab the player’s attention.
When a matching pair of cards is found by the player, fade those cards out, but use a pulsate transition when doing so.
This makes the cards “blink” smoothly and communicates the idea of accomplishment to the player (play a round of any jump and run video game and you’ll find similiar animations).
Remember the animation when you win a round of Solitaire on Windows? You can do better! The example code shows a combination of several animations. First, all cards are shown with the numbers revealed, next there’s a smoke-like upwards movement and finally the cards disappear. Use win() on the console (or win the game!) to see this. After a slight delay the game is reset.
14. Preloading card graphics
// preload images $R(1,8) is like [1,2,3,4,5,6,7,8] (for this purpose)
$R(1,8).each(function(id){
var img = new Image();
img.src = urlForId(id);
});
function urlForId(id){
return 'picture_'+id+'.png';
}
Because only the card backs are initially shown, preloading the card images makes sure there are no messed up card flipping animations.
15. Tweaking animation timings
The single most important thing with games is that everything should feel just right. Spending time on choosing the right transition animations, durations and delays is very important. This “motion design” is similiar to trying variations in graphic design. Don’t be shy and try out various settings and have fun fooling around. I’ll leave this step up to you!
Internet Explorer 8 is a bit peculiar when it comes to support CSS opacity. It still doesn’t support the ‘opacity’ property like any other browser, but also breaks support for ‘filter: alpha(opacity=XX)’, which worked fine in IE6 and IE7. To work around this, insert above meta element in the HEAD section of the HTML.
17. Avoiding “stuck” cards
Randomly reordering doesn’t guarantee that a card is in different positions on each shuffle (e.g. card #13 might be in position #6 on both random shuffles). To avoid this, set up a loop that keeps reshuffling until all card positions are swapped. Use Prototype’s zip and any functions to keep the code short and concise.
// randomize cards and reposition them randomly, in a 4x4 grid
// 1. make sure all positions change place
// this can take several iterations
// [].concat() is used to create a copy of an array
var currentOrder = [].concat(cards), matches = true;
while(matches){
// zip combines two arrays into one
// [1,2].zip(['a','b']) --> [[1, "a"], [2, "b"]]
// this can be used to compare two arrays (in this case, find "any" matching pairs)
cards = cards.sortBy(Math.random);
matches = cards.zip(currentOrder).any(function(pair) { return pair[0] === pair[1]; });
}
18. Final touches
It’s a good idea to harden your code, both against user intervention (wildly clicking on things!) and programmatically (wrapping it in its own namespace or JavaScript closure). In the demo code the “blocked” boolean is flipped when click events should be suppressed (lines 9, 109, 118, 132, 140), and everything is wrapped in a JavaScript closure pattern (lines 4, 157).
That’s it. Awesome browser games. No Flash required.
About the author
Thomas Fuchs has been writing hard-core JavaScript since waaaaay back in the late 1990s.
His famous script.aculo.us framework was created during the development of one of the most highly interactive applications the Web had ever seen. He’s continued to push the boundaries of what is possible with JavaScript, with Scriptaculous and its successor, Scripty2.
Script.aculo.us has gone on to be used in such web sites & applications as CNN.com, NASA.gov, Me.com and more.
In addition to being a Prototype core member and Rails core alumnus, Thomas is one of the world’s top JavaScript and rich web app performance experts. He is also the author of Textorize, an automated tool that brings beautifully anti-aliased image text to the web (and beats the pants off Photoshop).
Perhaps most importantly, we now rely on middleware for the CSS selector engine. It defaults to Sizzle (used in jQuery among others), but you can also plug in others that might work better for your specific application. A good alternative is NWMatcher, a VERY fast selector engine. You might also find this useful in cases you do not need to support some older browsers, or if you want to use Prototype for specific platforms like iPhone Safari, to cut down on unwanted code.
The coolest new feature is Element#on, an encapsulation of the event delagation pattern that is so helpful in cutting down on the number of event handlers:
$("messagelist").on("click", "li.active", function() {
// handle click on li elements with class "active"
});
Plus, Prototype 1.7 introduces a the new Element.Layout measurement methods, that allow for easy measuring of all sorts of CSS properties, automatic conversion to pixels of arbitrary CSS units plus measuring of hidden (display:none) elements.
Here’s my slide deck from my presentation at Webstock 2010 in Wellington, New Zealand, with me ranting about Flash (just a little!), and showing all sorts of up- and coming awesome web technologies (like HTML5 video, Canvas, SVG/VML and WebGL). Plus you can’t go wrong with a JavaScript-based C64 emulator.
Note that it’s really full of videos, something you regrettably can’t see when viewing the deck, but I do hope that the video of this talk will be up soon (will keep this post updated!).
The aerogel-weight mobile JavaScript framework, which also works great for Safari and Chrome extensions. The jQuery-compatible API makes it easy to pick up, and it's just 5k to 7k in size!