JS Proxying HTML5 canvas context

一世执手 提交于 2021-02-19 02:46:11

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!