GAMR1520: Markup languages and scripting

GAMR1520

Markup languages and scripting

The HTML canvas

Dr Graeme Stuart


Let’s look again at this example

We start with a red canvas element in our HTML document.

<canvas width="300" height="100" id="myCanvas" style="background: red;"></canvas>

The 2d canvas context provides us with access to the drawing API.

// get a reference to the 2d canvas context object
const ctx = myCanvas.getContext('2d');
    
// fill a big square (default to black)
ctx.fillRect(100, 0, 100, 100);
    
// fill two smaller squares
ctx.fillRect(30, 30, 40, 40);
ctx.fillRect(230, 30, 40, 40);
// change the fill style to red and draw another square
ctx.fillStyle = "red";
ctx.fillRect(130, 30, 40, 40);

Coordinates

Canvas coordinates are pretty simple but be aware that the origin is in the top left corner.


Paths, stroke() and fill()

<canvas width="300" height="100" id="myCanvas2" style="background: red;"></canvas>

Again, starting with a red canvas, this time we draw a path with individual lines. The path is closed (to join the end back with the beginning) and then filled in blue. We call stroke to draw a line on the path. Notice the thickness of the yellow line is spread equally over either side of the path.

const ctx2 = myCanvas2.getContext('2d');
ctx2.strokeStyle = "yellow";
ctx2.lineWidth = 5;
ctx2.fillStyle = "blue";
ctx2.moveTo(10, 10);
ctx2.lineTo(160, 40);
ctx2.lineTo(290, 10);
ctx2.lineTo(290, 90);
ctx2.lineTo(120, 60);
ctx2.lineTo(10, 90);
ctx2.closePath();
ctx2.fill();
ctx2.stroke();

Event listeners

Event Listeners allow us to execute code when certain events occur on an element. The classic example is the click event which triggers when the mouse clicks an element.

<canvas width="300" height="150" id="myCanvas3" style="background: #eee; border: 4px solid black;"></canvas>

When we click on the canvas, we generate random coordinates x and y based on random numbers (0 to 1) multiplied by the width (for x) and the height (for y) of the canvas. We also generate a random radius value, r. We use these coordinates to as the location of a circle which we draw on the canvas.

const ctx3 = myCanvas3.getContext('2d');
ctx3.strokeStyle = "hsla(30, 50%, 40%, 0.15)";
ctx3.lineWidth = 20;
function draw_random_circle() {
    const x = Math.random() * myCanvas3.width;
    const y = Math.random() * myCanvas3.height;
    const r = (0.1 + Math.random()) * myCanvas3.height / 4;
    ctx3.beginPath();
    ctx3.arc(x, y, r, 0, Math.PI * 2);
    ctx3.stroke();
}
myCanvas3.addEventListener('click', draw_random_circle);

Mouse events

Here we turn drawing on when the mousedown event fires and turn it off when the mouseup event fires. We also call lineTo on the mousemove event, but only when drawing is turned on.

const ctx = canvas.getContext('2d');
let on = false;
canvas.addEventListener('mousedown', ev => {
    on = true;
    ctx.moveTo(ev.offsetX, ev.offsetY);
    ctx.beginPath();
});
canvas.addEventListener('mouseup', ev => {
    on = false;
});
canvas.addEventListener('mousemove', ev => {
    if(on) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.lineTo(ev.offsetX, ev.offsetY);
        ctx.stroke();
    }
});

Lines and curves

A path is a series of points connected by lines or curves. We can use moveTo, lineTo, bezierCurveTo, quadraticCurveTo and arcTo to join paths in different ways.

Use quadraticCurveTo to add a quadratic bezier curve to the current sub-path. The curve begins at the latest point in the current path. The function requires coordinates for a control point plus the end point.

Use bezierCurveTo to add a cubic bezier curve to the current sub-path. The curve begins at the latest point in the current path. The function requires coordinates for two control points plus the end point.

Use arcTo to add a circular arc between two points. The function requires coordinates for two control points plus the radius of the arc. The examples here use the same control point but different radii.


Animation

To animate a canvas, we just need to draw one frame at a time. The frame function clears the canvas and draws a square at the given coordinates. We just move the x-coordinate a little bit every frame.

const animatedCtx = animatedCanvas.getContext("2d");
animatedCtx.fillStyle = "red";
let animx = 200;
let animy = 75;
function animateFrame() {
    animatedCtx.clearRect(0, 0, animatedCanvas.width, animatedCanvas.height)
    animatedCtx.fillRect(animx - 10, animy - 10, 20, 20);
    animx += 25;
    animx %= animatedCanvas.width;
}
animateFrame();
animatedCanvas.addEventListener('click', ev => {
    animateFrame();
});

What’s next?

Now we are working with the HTML <canvas> element, your job is to experiment.

Next week, we will be covering how to use JavaScript classes to represent objects in a scene. And you will be asked to produce another assignment in which you will produce an animation of your choice.

Thanks for listening

Any questions?

We should have plenty of time for questions and answers.

Just ask, you are probably not the only one who wants to know.

Dr Graeme Stuart