问题
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?
回答1:
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
.
For whatever reason, the setter on CanvasRenderingContext2DPrototype.strokeStyle
will accept only a genuine CanvasRenderingContext2D
instance, not a proxied one.
来源:https://stackoverflow.com/questions/33804653/js-proxying-html5-canvas-context