简体   繁体   中英

JS Proxying HTML5 canvas context

I'm hoping to proxy the canvas API so I can test that abstracted methods do actually draw to the canvas, however I'm hitting issues where after proxing I get an error:

'strokeStyle' setter called on an object that does not implement interface CanvasRenderingContext2D

This code is simplified but throws the same error:

 /** !NB: This snippet will probably only run in Firefox */ var canvas = document.createElement("canvas"); canvas.width = 100; canvas.height = 100; canvas.style.backgroundColor = '#FF0000'; var ctx = canvas.getContext("2d"); var calls = []; var handler = { get( target, property, receiver ) { if ( typeof ctx[property] === 'function' ){ return function( ...args ){ calls.push( { call: property, args: args } ) return ctx[property]( ...args ); }; } return ctx[property]; } }; try { document.body.appendChild(canvas); var proxy = new Proxy( ctx, handler ); proxy.scale( 1, 1 ); proxy.strokeStyle = '#000000'; canvas.getContext = function(){ return proxy; }; } catch( e ) { document.getElementById('message').innerHTML = 'Error: ' + e.message; } 
 <div id="message"></div> 

Any thoughts?

You can fix this erro by defining a set method on your handler:

set(target, property, value, receiver) {
    target[property] = value;
}

The reason for this error might seem a bit strange. CanvasRenderingContext2D instances don't have their own strokeStyle property. Instead, the CanvasRenderingContext2DPrototype (the prototype of every CanvasRenderingContext2D instance) has an accessor property whose set / get components will set and get the stroke-style value for the instance:

> ctx.hasOwnProperty("strokeStyle")
false

> Object.getOwnPropertyDescriptor(ctx.__proto__, "strokeStyle")
Object { get: strokeStyle(), set: strokeStyle(), enumerable: true, configurable: true }

(If you're interested in learning more about this pattern, have a look at my answer on JSON.parse not erroring on cyclic objects .)

The problem here is that the this supplied to the CanvasRenderingContext2DPrototype.strokeStyle setter is the proxy object, not the actual ctx object. That is, when we set a property on the proxy only:

proxy.isAFake = true;

and test for it in a redefined setter:

Object.defineProperty(ctx.__proto__, "strokeStyle", {
    set: function() {
        console.log("strokeStyle setter called for proxy?", this.isAFake);
    }
});

We see the setter logs the proxy-only property: strokeStyle setter called for proxy? true strokeStyle setter called for proxy? true .

For whatever reason, the setter on CanvasRenderingContext2DPrototype.strokeStyle will accept only a genuine CanvasRenderingContext2D instance, not a proxied one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM