Unable to Use CanvasRenderingContext2D Methods with Variable Assignment

Viewed 118

I am working on a webapp that uses the canvas to draw a lot of stroked and/or filled rectangles using the HTML/JavaScript Canvas API. Since I have to use the exact same coordinates for both stroked and filled rectangles, I want to assign the current drawing function (either ctx.strokeRect or ctx.fillRect) to a variable and call it later.

Unfortunately, trying to use a variable holding a CanvasRenderingContext2D function throws an error: 'fillRect' called on an object that does not implement interface CanvasRenderingContext2D.

Here is a simplified version of what I'm trying to do, which throws an error drawing the second rectangle:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// draw the top rectangle - works fine
ctx.fillRect(0, 0, 100, 50);
// draw the bottom rectangle - crashes with an error
(f => f(0, 50, 100, 50))(ctx.fillRect);
<canvas id="canvas" width="100" height="100"></canvas>

An error is also thrown if the method is assigned to a variable, such as:

const fn = ctx.fillRect;
fn(0, 0, 50, 50);

Why can't canvas context functions can't be used this way? Or can they be used this way, and am I missing a mistake in the code?

1 Answers

Because these methods are inherited from the CanvasRenderingContext2D prototype, and are the same exact function for all instances of such a context.

const canvas1 = document.createElement("canvas");
const canvas2 = document.createElement("canvas");
const ctx1 = canvas1.getContext("2d");
const ctx2 = canvas2.getContext("2d");
const fn1 = ctx1.fillRect;
const fn2 = ctx2.fillRect;

console.log( "fn1 === fn2:", fn1 === fn2 );
console.log( "fn1 === proto.fillRect:", fn1 === CanvasRenderingContext2D.prototype.fillRect );

What you want is to create a copy of this function that is bound to your particular instance of a context:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// draw the top rectangle - works fine
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = "green";
(f => f(0, 50, 100, 50))(ctx.fillRect.bind(ctx));
<canvas id="canvas" width="100" height="100"></canvas>

Related