Dit is het derde en laatste deel van een serie blogs over het HTML5 Canvas. In deel 1 en deel 2 hebben we gekeken naar het canvas element. Hoe kunnen we tekenen op het canvas, het manipuleren van de context en het werken met afbeeldingen en video's. In dit deel gaan we kijken naar de mogelijkheden voor animatie en Interactie.

Dit is het derde en laatste deel van een serie blogs over het HTML5 Canvas. In deel 1 en deel 2 hebben we gekeken naar het canvas element. Hoe kunnen we tekenen op het canvas, het manipuleren van de context en het werken met afbeeldingen en video's. In dit deel gaan we kijken naar de mogelijkheden voor animatie en Interactie.
Animeren met het canvas
Het Canvas zelf biedt eigenlijk geen enkele functionaliteit voor het toevoegen van animaties. Aangezien er geen DOM bijgehouden wordt kun je dus ook niet aangeven dat een bepaald object zich moet verplaatsen. Toch is het mogelijk om animaties te maken, al lijkt het op het eerste gezicht misschien erg omslachtig.
Een object over het scherm verplaatsen gaat als volgt:
We beginnen met het tekenen van de achtergrond en het te verplaatsen object. Het animeren is dan het herhalen van de onderstaande stappen:
  1. Teken de achtergrond opnieuw zodat het object niet meer te zien is.
  2. Bepaal de nieuwe locatie waar het object getekend moet worden.
  3. Teken het object opnieuw.
Deze herhalingen kunnen we plaats laten vinden door middel van de setInterval() of setTimeout() functie.
var step = 1;
var ctx;
function initialize() {
    var canv = document.getElementById('voorbeeld'); ;
    ctx = canv.getContext('2d');
    ctx.lineWidth = 10;
    ctx.strokeStyle = '#ff6622';
    ctx.fillStyle = 'rgba(200, 200, 255, 1)';
    moveObject();
}
function moveObject(){
    // teken achtergrond
    ctx.fillRect(0, 0, 600, 300);
    // bereken positie
    xpos = step * 50;
    ypos = step * 20;
    // teken object
    ctx.strokeRect(xpos, ypos, 30, 30);
    step++;
    if (step <= 10) {
        setTimeout('moveObject()', 100);
    }
}

Animatie en interactie
Als je de frequentie verhoogt (dus de intervaltijd verkleint) zal de animatie soepeler worden. Echter heeft het geen zin om sneller te updaten dan de refresh-rate van de monitor, oftewel 60 keer per seconde.
Het is aan je eigen creativiteit hoe je een object laat bewegen en hoeveel objecten je simultaan animeert. Je zult het alleen zelf in code moeten oplossen. Zo is het bijvoorbeeld mogelijk om zelf "ease-in" en "ease-out" effecten toe te voegen, maar ook dit zul je zelf moeten programmeren. Hier kun je bijvoorbeeld wiskundige berekeningen voor gebruiken, zoals de Cosinus-functie. Het voorgaande voorbeeld kunnen we als volgt aanpassen.
// bereken positie
progressWithEasyInOut = (1- (Math.cos((step / 10) * (Math.PI)))) / 2;
xpos = 500 * progressWithEasyInOut;
ypos = 200 * progressWithEasyInOut;
// teken object
ctx.strokeRect(xpos, ypos, 30, 30);

Animatie en interactie
Relatieve animaties
Tot nu toe hebben we een animatie gebruikt die een vaste begin- en eindpositie heeft en daar in een vast aantal stappen naartoe beweegt. Er is een andere manier van animeren, namelijk door het bijhouden van de positie van het object en de nieuwe positie te berekenen naar aanleiding van de huidige positie.
Stel we willen een bal door het beeld laten stuiteren, dan bewaren we de X en Y positie en X en Y snelheid van het object in variabelen. Zo kunnen we telkens de volgende positie uitrekenen, immers de nieuwe positie = de oude positie + de snelheid. Als je een stap verder gaat kun je de snelheid bij elke nieuwe berekening iets laten afnemen, zodat het lijkt of er "luchtwrijving" is. Ook zou je de Y snelheid telkens met een vaste waarde kunnen ophogen zodat er ook "zwaartekracht" lijkt te zijn.
var currentPositionX, currentPositionY;
var currentSpeedX, currentSpeedY;
function MoveObject() {
    var gravity = 0.1;
    var easyoutPrecentage = 0.98;
    // "kopieer" het stuk context naar nu de bal is.
    var imagedata = context.getImageData(Math.round(currentPositionX),
                                         Math.round(currentPositionY),
                                         50,
                                         50);
    // vul het stukje achtergrond waar de bal nu getekend is.
    context.fillRect(Math.round(currentPositionX), Math.round(currentPositionY), 50, 50);
    // bereken de nieuwe positie
    currentPositionX = (currentPositionX + currentSpeedX);
    currentPositionY = (currentPositionY + currentSpeedY);
    // teken de bal op de nieuwe positie
    context.putImageData(imagedata,
                         Math.round(currentPositionX),
                         Math.round(currentPositionY));
    // wanneer de rand van het canvas geraakt wordt,
    // moet de bal de andere kant op "stuiten"
    if ((currentPositionX < 25) || (currentPositionX > 725)) {
        currentSpeedX = -currentSpeedX;
    }
    if ((currentPositionY < 25) || (currentPositionY > 525)) {
        currentSpeedY = -currentSpeedY;
    }
    // Easy out
    currentSpeedX = (currentSpeedX * easyoutPrecentage);
    currentSpeedY = (currentSpeedY * easyoutPrecentage) + gravity;
           
    // Stoppen als de snelheid te verwaarlozen is
    if ((Math.abs(currentSpeedY) > 0.1) || (Math.abs(currentSpeedX) > 0.1)) {
        setTimeout('moveObject()', 20);
    } else {
        currentSpeedY = 0;
        currentSpeedX = 0;
    }
}

Animatie en interactie
In de bovenstaande code staan twee functies die nog niet genoemd zijn: getImageData() en putImageData(). Deze kunnen gebruikt worden om een stuk canvas te kopiëren en te plakken. Dit komt van pas wanneer het te verplaatsen gedeelte uit meerdere obejcten bestaat en dus diverse tekenacties zou vereisen.
Interactie
Het voorbeeld met de stuiterende bal wordt natuurlijk helemaal interessant als de gebruiker de bal zelf zou kunnen gooien. Daarvoor is interactie met de gebruiker nodig. Net als alle andere HTML elementen kunnen we van het Canvas de mouse-events afvangen. Het mouseMove event kunnen we gebruiken om het object te tekenen. Niet op een vast berekende positie, maar naar aanleiding van de positie van de muis. Het object lijkt dan vast te plakken aan de cursur. Door de mouseDown en mouseUp events te gebruiken kunnen we ervoor zorgen dat het object alleen aan de cursor plakt wanneer de muisknop ingedrukt is.
function initialize() {
    var img;
    var canv = document.getElementById('voorbeeld');
    var prev_x = 35;
    var prev_y = 35;
    var dragging = false;
    var ctx = canv.getContext('2d');
    context.fillStyle = '#222222';
    context.fillRect(0, 0, 800, 600);
    img = new Image();
    img.src = 'images/strtbtn.png';
    img.onload = function () {
        ctx.drawImage(img, 10, 10, 50, 50);
    }
    ctx.save();
    canv.onmousedown = function (e) {
        dragging = true;
    }
    canv.onmouseup = function (e) {
        dragging = false;
    }
    canv.onmousemove = function (e) {
        if (dragging == true) {
            x = e.clientX - e.target.offsetLeft;
            y = e.clientY - e.target.offsetTop;
            ctx.fillRect(prev_x - 25, prev_y - 25, 50, 50);
            ctx.drawImage(img, x - 25, y - 25, 50, 50);
            prev_x = x;
            prev_y = y;
        }
    }
}
In het mouseMove event berekenen we vervolgens de snelheid van het object. Als de vorige X en Y posities in variabelen worden opgeslagen en deze worden vergeleken met de huidige X en Y posities, dan weet je dus de afstand die is afgelegd sinds de laatste berekening. Als je ook de tijd tussen de berekeningen vergelijkt, dan heb je de snelheid te pakken. (Snelheid = afstand / tijd) In het vorige voorbeeld gebruikten we deze snelheid om het object zelfstandig te laten bewegen. Het resultaat is een voorwerp dat je met de muis kunt verplaatsen, een beweging mee kunt geven en vervolgens maakt het object de beweging zelfstandig af. Dit kunnen we gebruiken op tal van manieren: verplaatsen, ronddraaien, gooien, enz.
Overigens zijn de mouse events op veel meer manieren te gebruiken, Denk bijvoorbeeld aan situaties waarin de gebruiker zelf kan tekenen of markeren op het canvas.
Conclusie
Het is enigszins afhankelijk van welke statistieken je bekijkt, maar in ieder geval heeft ruim de helft van de internetgebruikers op dit moment een browser die HTML5 Canvas ondersteunt. Gelukkig is het ook goed mogelijk om fallback content aan te bieden voor de groep gebruikers die hoognodig toe is aan een nieuwe browser. Kijkend naar de toekomst is iedereen het er over eens dat HTML5 een zeer belangrijke rol gaat spelen. Dit artikel heeft de basis laten zien van de mogelijkheden en de methodieken rond het gebruik van het Canvas. Op het eerste gezicht biedt het Canvas dan wel weinig functionaliteit voor animatie en moet alles gescript worden, maar de code blijkt relatief simpel en je bent onafhankelijk van plugins, browser en platform. En.. jij maakt de code, dus "the sky is the limit!"