Skip to content
Advertisement

JS: Resizing canvas + redrawing elements on canvas

My question: What’s the best way to resize the canvas with all its drawn elements?

I have 2 examples where I tried to archieve this.

The first one works, but lacks functionality because if I would add many more elements to be drawn it would get very messy. In my second example I tried to put everything into an array and loop through it but it doesn’t resize correctly anymore because it doesn’t recalculate the x,y width and height anymore. Basically I wish to know a way to not have to call a function which calculates the elements x,y,width and height again before drawing. I want to to put the math into the array and when it’s getting drawn, it recalculates so I just have to make sure the canvas.width/height is updated, how do I do that?

1st example

class Canvas {
    constructor() {
        this.canvas = document.createElement('canvas');
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
        this.ctx = this.canvas.getContext('2d');
        document.body.appendChild(this.canvas);
        this.resize();
    }
    
    initAreas() {
        this.firstArea = { "x": 0, "y": 0, "w": this.canvas.width, "h": this.canvas.height / 2 }
        this.secondArea = { "x": 0, "y": this.firstArea.h, "w": this.canvas.width, "h": this.canvas.height / 2 }
    }
    
    resize() {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
        this.initAreas();
        this.ctx.fillStyle = "green";
        this.ctx.fillRect(this.firstArea.x, this.firstArea.y, this.firstArea.w, this.firstArea.h);
        this.ctx.fillStyle = "red";
        this.ctx.fillRect(this.secondArea.x, this.secondArea.y, this.secondArea.w, this.secondArea.h);
    }

}

const canvas = new Canvas();

window.onresize = function() { canvas.resize(); }
body {
    padding: 0;
    margin: 0;
}

canvas {
    display: block;
}
<!DOCTYPE html>

<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <script src="script.js"></script>
</body>
</html>

2nd example

class Canvas {
constructor() {
    this.canvas = document.createElement('canvas');
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    this.ctx = this.canvas.getContext('2d');
    document.body.appendChild(this.canvas);
    this.areasToDraw = [];
    this.initAreas();
    this.resize();
}

initAreas() {
    this.firstArea = { "x": 0, "y": 0, "w": this.canvas.width, "h": this.canvas.height / 2, "color": "green" }
    this.areasToDraw.push(this.firstArea);
    this.secondArea = { "x": 0, "y": this.firstArea.h, "w": this.canvas.width, "h": this.canvas.height / 2, "color": "red" }
    this.areasToDraw.push(this.secondArea);
}

resize() {
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    for(this.areas in this.areasToDraw) {
        this.ctx.fillStyle = this.areasToDraw[this.areas].color;
        this.ctx.fillRect(this.areasToDraw[this.areas].x, this.areasToDraw[this.areas].y, this.areasToDraw[this.areas].w, this.areasToDraw[this.areas].h);
    }
}

Advertisement

Answer

Observation 1: “Since resize events can fire at a high rate, the event handler shouldn’t execute computationally expensive operations such as DOM modifications. Instead, it is recommended to throttle the event using requestAnimationFrame, setTimeout or customEvent” This is from MDN resize

Observation 2: on resize you need to bind the canvas, otherwise you will get canvas undefined.

Observation 3: when you resize the canvas, you need to empty the array of elements: this.areasToDraw in this case, and repopulate the array with new elements.

This is how I would do it:

class Canvas {
constructor() {
    this.canvas = document.createElement('canvas');
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    this.ctx = this.canvas.getContext('2d');
    document.body.appendChild(this.canvas);
    this.areasToDraw = [];
    //this.initAreas();
    //this.resize();
   
}

initAreas() {
    this.firstArea = { "x": 0, "y": 0, "w": this.canvas.width, "h": this.canvas.height / 2, "color": "green" }
    this.areasToDraw.push(this.firstArea);
    this.secondArea = { "x": 0, "y": this.firstArea.h, "w": this.canvas.width, "h": this.canvas.height / 2, "color": "red" }
    this.areasToDraw.push(this.secondArea);
}

resize() {
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    // empty the array
    this.areasToDraw.length = 0;
    // repopulate the array
    this.initAreas();
    for(this.areas in this.areasToDraw) {
        this.ctx.fillStyle = this.areasToDraw[this.areas].color;
        this.ctx.fillRect(this.areasToDraw[this.areas].x, this.areasToDraw[this.areas].y, this.areasToDraw[this.areas].w, this.areasToDraw[this.areas].h);
    }
}

}

let canvas = new Canvas()

setTimeout(function() {
		canvas.resize()
		addEventListener('resize', canvas.resize.bind(canvas), false);
}, 15);
body {
	padding: 0;
	margin: 0;
}

canvas {
	display: block;
}
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement