WebGL Animation

Your browser does not support the canvas tag. This is a static example of what would be seen.

This is the second in a series of posts about using WebGL. So far I’ve made a small green rectangle (exciting!), and this builds on that by adding animation to it. Hover on the above square to see animation in action.

I’m not quite ready to get into actual rendering code just yet… so no shaders, buffers, etc… but I want to do enough to prove that I can update a canvas at regular intervals and I’m happy for now just to clear to a random colour every 1/30th of a second, just so I have some evidence that the updates are actually happening.

The code here, which builds on my earlier example, does just that, and a bit more…


var loadIntoCanvas = function(canvas, frameRate, client_onLoad, client_onRender) {

  var gl;

  var initialWidth = 0
  var initialHeight = 0;

  var updateRequest;
  var frameDelta = 0;
  var then;

  function setCanvasSize() {

    /* match the canvas size to the containing div */
    var parentSize = canvas.parentNode.parentNode.getBoundingClientRect();
    if (parentSize.width < initialWidth) {
      canvas.width = initialWidth / 2;
      canvas.height = initialHeight / 2;
    } else {
      canvas.width = initialWidth;
      canvas.height = initialHeight;
    }
	    
  }

  /* called when the canvas needs to be redrawn */
  function onRender() {

    /* update the canvas size */
    setCanvasSize();

    /* redraw the canvas */
    client_onRender(gl, canvas);

  }

  function onAnimationUpdate() {

    /* request another animation call ASAP */
    updateRequest = requestAnimationFrame(onAnimationUpdate);

    /* trigger calls to onRender at regular intervals */
    var now = Date.now();
    var elapsed = now - then;
    if (elapsed > frameDelta) {
      then = now - (elapsed % frameDelta);
      onRender();
    }  

  }

  /* called when the canvas is first loaded */  
  function onLoad() {

    /* initialise WebGL */	
    gl = canvas.getContext("webgl"); 
    if (gl == null)
      gl = canvas.getContext("experimental-webgl"); 

    /* initial update of the canvas size */
    initialWidth = canvas.width;
    initialHeight = canvas.height;
    setCanvasSize();

    /* record the initial time */
    then = Date.now();

    /* give the client a chance to initialise against the gl context */
    client_onLoad(gl, canvas);

    /* start and stop the animation based on mouse events */
    if (frameRate > 0.0) {
      frameDelta = Math.floor(1000.0 / frameRate);
      canvas.addEventListener('mouseenter', 
        function(){ requestAnimationFrame(onAnimationUpdate) }, false)
      canvas.addEventListener('mouseleave', 
        function(){ cancelAnimationFrame(updateRequest) }, false)
    }

    /* trigger a redraw (and a canvas resize whenever a resize occurs */
    window.addEventListener('resize', onRender(), false);

    /* initial render of the canvas */
    onRender();

  }  

  onLoad();

};

loadIntoCanvas(
  document.getElementById("gl_animatedSquare"),
  30.0,
  function(gl, canvas) {
    /* setup would go here */
  },
  function(gl, canvas) {
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.clearColor(Math.random(), Math.random(), Math.random(), 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);      
  });

The code that’s most relevant to animation support is highlighted.

On initializing the canvas we record the current time using Data.now(), and assuming animation is requested (frameRate not 0) we store a frame-delta (time between frames) to be used during the animation updates to work out if a new frame is due. We attach two listeners that activate and deactivate the animation as the mouse moves over and leaves the canvas area.

Our animation function will end up being called more often than we would like, so we track the time elapsed since the last redraw and issue calls to onRender as required. The an8imation function also requests another animation tick, so it will keep getting called over and over.

Leave a Reply

Your email address will not be published. Required fields are marked *