Computer Science 15-237
Class Notes:  Getting Started with HTML5 Canvas


  1. Required Reading
    1. MDN Canvas Tutorial
    2. w3schools Canvas Tutorial
       
  2. Canvas (the bare minimum)
    1. "Hello World" HTML
      <html>
      <canvas id="myCanvas" width="400" height="400"
              style="border:1px solid #000000;">
          No canvas available!
      </canvas>
      <script src="test.js">
      </script>
      </html>
    2. "Hello World" Javascript (in test.js)
      var canvas = document.getElementById("myCanvas");
      var ctx = canvas.getContext("2d");
      ctx.font = "60px Arial";
      ctx.textAlign = "center";
      ctx.fillText("Hello World!", 200, 200);
  3. Coordinate System
    1. (0,0) is left-top
    2. "up is down"
    3. Fractional pixels
      1. antialiasing simulates fractional pixels
      2. lines are centered with half a line width on either side
      3. so the crispest lines have edges at integer values
      4. and integer values may run much faster, too!
         
  4. Rectangles

    // fill a black (default) 50x100 rectangle with left-top at (50, 150)
    ctx.fillRect(50, 150, 50, 100);

    // fill a blue rectangle next to the black one
    ctx.fillStyle = "#0000FF";
    ctx.fillRect(150, 150, 50, 100);

    // outline a red rectangle across them
    ctx.strokeStyle = "red";
    ctx.strokeRect(25, 175, 200, 100);

     
  5. More Strokes and Fills
    1. Specifying Colors

      ctx.fillStyle = "blue";              // HTML4 name
      ctx.fillStyle = "#0000FF";           // hex RRGGBB
      ctx.fillStyle = "#00F";              // hex RGB
      ctx.fillStyle = "rgb(0, 0, 255)";    // decimal 0-255
      ctx.fillStyle = "rgb(0%, 0%, 100%)"; // percentages
                                           // there's also HSL (look it up!)
    2. Alpha Transparency
      1. rgba

        ctx.fillRect(100, 100, 200, 200);
        ctx.fillStyle = "rgb(0, 0, 255)";
        ctx.fillRect(50, 50, 300, 100);
        ctx.fillStyle = "rgba(0, 0, 255, 0.5)";
        ctx.fillRect(50, 250, 300, 100);
         
      2. globalAlpha

        ctx.fillRect(100, 100, 200, 200);
        ctx.fillStyle = "rgb(0, 0, 255)";
        ctx.fillRect(50, 50, 300, 100);
        ctx.globalAlpha = 0.5;
        ctx.fillRect(50, 250, 300, 100);

         
  6. Paths
    1. Filled Path

      ctx.beginPath();
      ctx.moveTo(150, 50);
      ctx.lineTo(100, 100);
      ctx.lineTo(200, 100);
      ctx.fill();
       
    2. Stroked Path

      ctx.beginPath();
      ctx.moveTo(150, 50);
      ctx.lineTo(100, 100);
      ctx.lineTo(200, 100);
      ctx.closePath();
      ctx.stroke();
       
  7. Arcs

    ctx.beginPath();
    var cx = 200;
    var cy = 200;
    var radius = 100;
    var startAngle = 0; // radians from x axis
    var endAngle = Math.PI/2; // ditto
    var anticlockwise = true;
    ctx.arc(cx, cy, radius, startAngle, endAngle, anticlockwise);
    ctx.fill();

     
  8. Circles

    function circle(ctx, cx, cy, radius) {
        ctx.arc(cx, cy, radius, 0, 2*Math.PI, true);
    }
    ctx.beginPath();
    var cx = 200;
    var cy = 200;
    var radius = 100;
    circle(ctx, cx, cy, radius);
    ctx.fill();
     
  9. Rounded Rectangles

    // from: https://developer.mozilla.org/en-US/docs/Canvas_tutorial/Drawing_shapes
    function roundedRect(ctx,x,y,width,height,radius){
        ctx.beginPath();
        ctx.moveTo(x,y+radius);
        ctx.lineTo(x,y+height-radius);
        ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
        ctx.lineTo(x+width-radius,y+height);
        ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
        ctx.lineTo(x+width,y+radius);
        ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
        ctx.lineTo(x+radius,y);
        ctx.quadraticCurveTo(x,y,x,y+radius);
        ctx.stroke();
    }
    roundedRect(ctx, 100, 100, 200, 200, 50);
     
  10. Text

    ctx.font = "60px Arial";
    ctx.textAlign = "center";
    ctx.fillStyle = "blue";
    ctx.fillText("Hello World!", 200, 200);

    var text = "Go Steelers!";
    ctx.font = "bold italic 30px sans-serif";
    ctx.textAlign = "right";
    ctx.textBaseline = "middle";
    ctx.strokeStyle = "green";
    ctx.strokeText(text, 400, 100);

    var width = ctx.measureText(text).width;
    ctx.strokeStyle = "orange";
    ctx.strokeRect(400-width,100-30/2,width,30);
     
  11. Transformations
    1. The Canvas Stack (save/restore)
      state == styles + transformations (+ clipping paths)  [applies to future drawing, not past drawing!]
      save == push state onto stack
      restore == pop state off of stack
       
    2. Translate

      function drawThing() {
          ctx.beginPath();
          ctx.moveTo(100, 100);
          ctx.lineTo(0, 100);
          ctx.lineTo(0, 0);
          ctx.lineTo(50, 50);
          ctx.lineTo(100, 0);
          ctx.closePath();
          ctx.stroke();
      }

      drawThing();
      ctx.save();

      ctx.translate(200, 50);
      ctx.strokeStyle = "red";
      drawThing();

      ctx.translate(50, 75);
      ctx.strokeStyle = "green";
      drawThing();

      ctx.restore();
      ctx.translate(50, 75);
      ctx.strokeStyle = "blue";
      drawThing();
       
    3. Other Transformations (rotate, scale, transform)
      rotate(angle):  rotate by angle around the current origin
      scale(xfactor, yfactor):  1.0 means unscaled, negative numbers reflects over axes
      transform(m11, m12, m21, m22, dx, dy): provide an arbitrary transform matrix (look it up!)
       
  12. Mouse Events

    function onMouseDown(event) {
        var x = event.pageX - canvas.offsetLeft;  // do not use event.x, it's not cross-browser!!!
        var y = event.pageY - canvas.offsetTop;
        ctx.fillStyle = "rgba(0,128,128,0.5)";
        ctx.fillRect(x-25, y-25, 50, 50);
    }
    canvas.addEventListener('mousedown', onMouseDown, false);

     
  13. Keyboard Events

    function onKeyDown(event) {
        alert('keyCode: ' + event.keyCode);
    }

    canvas.addEventListener('keydown', onKeyDown, false);

    // make canvas focusable, then give it focus!
    canvas.setAttribute('tabindex','0');
    canvas.focus();
     
  14. Timer Events
    Note: Examples here update a fixed amount every interval.  But interval times vary extensively by device, so you will want to take elapsed time into account especially when you write apps for phones.  This is not necessary for hw1, however.
    1. With setInterval

      var canvas = document.getElementById("myCanvas");
      var ctx = canvas.getContext("2d");

      var intervalId;
      var timerDelay = 100;
      var rectLeft = 0;
      var quit = false;

      function redrawAll() {
          // erase everything -- not efficient, but simple!
          ctx.clearRect(0, 0, 400, 400);
          ctx.fillRect(rectLeft, 180, 40, 40);
          ctx.font = "20px Arial";
          ctx.fillText("Press 'q' to quit.", 20, 40);
      }

      function onTimer() {
          if (quit) return;
          rectLeft = (rectLeft + 10) % 400;
          redrawAll();
      }

      function onKeyDown(event) {
          if (quit) return;
          var qCode = 81;
          if (event.keyCode === qCode) {
              clearInterval(intervalId);
              // ctx.clearRect(0, 0, 400, 400);
              ctx.fillStyle = "rgba(128,128,128,0.75)";
              ctx.fillRect(0, 0, 400, 400);
              quit = true;
          }
      }

      function run() {
          canvas.addEventListener('keydown', onKeyDown, false);
          // make canvas focusable, then give it focus!
          canvas.setAttribute('tabindex','0');
          canvas.focus();
          intervalId = setInterval(onTimer, timerDelay);
      }

      run();

       
    2. with setTimeout

      var intervalId;

      function redrawAll() {
          ctx.clearRect(0, 0, 400, 400);
          ...
      }

      function onTimer() {
          ...
          setTimeout(onTimer, timerDelay);
      }

      function onKeyDown(event) {
          ...
              clearInterval(intervalId);
          ...
      }

      function run() {
          ...
          intervalId = setInterval(onTimer, timerDelay);
          setTimeout(onTimer, timerDelay);
      }

      run();

       
    3. with requestAnimationFrame
      See MDN doc.
       
  15. Images and Sprites
    1. Draw image full-size at (x,y)
      var img = new Image();
      img.src = "sampleImage.jpg";
      img.onload = function(){
      	var canvas = document.getElementById("myCanvas");
      	var ctx = canvas.getContext("2d");
      	ctx.drawImage(img, 0, 0);  // draw image at (0, 0)
      };
    2. Draw image with specific dimensions
      	ctx.drawImage(img, 0, 0, 150, 150); // resize to 150x150
    3. Draw a portion of a graphic (useful for sprite sheets)
      	//drawing a slice of the image at a specific coordinate in a specific size
      	//slice: taken from (sx, sy) relative to the top left of the image
      	//slice: of width sWidth and height sHeight
      	//slice: drawn at (dx, dy) on the canvas
      	//slice: drawn with width dWidth and height dHeight on the canvas
      	//ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
      	ctx.drawImage(img, 50, 50, 250, 250, 200, 200, 100, 100);
  16. Not included here
    1. Gradients
    2. Patterns (images as fill styles)
    3. Bezier and Quadratic Curves
    4. Shadowed Text
    5. Line Styles (lineWidth, lineCap, lineJoin, miterLimit)
    6. Compositing Shapes
    7. Clipping Paths
    8. 3d Context
    9. And more...!

carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem