JS 代理 HTML5 canvas 上下文
JS Proxying HTML5 canvas context
我希望代理 canvas API 以便我可以测试抽象方法是否确实绘制到 canvas,但是在代理之后我遇到了问题得到一个错误:
'strokeStyle' setter called on an object that does not implement interface CanvasRenderingContext2D
此代码已简化但会引发相同的错误:
/** !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>
有什么想法吗?
您可以通过在处理程序上定义 set
方法来修复此错误:
set(target, property, value, receiver) {
target[property] = value;
}
这个错误的原因可能看起来有点奇怪。 CanvasRenderingContext2D
个实例没有自己的 strokeStyle
属性。相反,CanvasRenderingContext2DPrototype
(每个 CanvasRenderingContext2D
实例的原型)有一个访问器 属性,其 set
/get
组件将设置和获取 stroke-style 值例如:
> ctx.hasOwnProperty("strokeStyle")
false
> Object.getOwnPropertyDescriptor(ctx.__proto__, "strokeStyle")
Object { get: strokeStyle(), set: strokeStyle(), enumerable: true, configurable: true }
(如果您有兴趣了解有关此模式的更多信息,请查看我在 JSON.parse not erroring on cyclic objects 上的回答。)
这里的问题是提供给 CanvasRenderingContext2DPrototype.strokeStyle
setter 的 this
是 proxy
对象,而不是实际的 ctx
对象。也就是说,当我们只在代理上设置一个 属性 时:
proxy.isAFake = true;
并在重新定义的 setter:
中测试它
Object.defineProperty(ctx.__proto__, "strokeStyle", {
set: function() {
console.log("strokeStyle setter called for proxy?", this.isAFake);
}
});
我们看到 setter 记录仅代理 属性:strokeStyle setter called for proxy? true
。
无论出于何种原因,CanvasRenderingContext2DPrototype.strokeStyle
上的 setter 将只接受真正的 CanvasRenderingContext2D
实例,而不是代理实例。
我希望代理 canvas API 以便我可以测试抽象方法是否确实绘制到 canvas,但是在代理之后我遇到了问题得到一个错误:
'strokeStyle' setter called on an object that does not implement interface CanvasRenderingContext2D
此代码已简化但会引发相同的错误:
/** !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>
有什么想法吗?
您可以通过在处理程序上定义 set
方法来修复此错误:
set(target, property, value, receiver) {
target[property] = value;
}
这个错误的原因可能看起来有点奇怪。 CanvasRenderingContext2D
个实例没有自己的 strokeStyle
属性。相反,CanvasRenderingContext2DPrototype
(每个 CanvasRenderingContext2D
实例的原型)有一个访问器 属性,其 set
/get
组件将设置和获取 stroke-style 值例如:
> ctx.hasOwnProperty("strokeStyle")
false
> Object.getOwnPropertyDescriptor(ctx.__proto__, "strokeStyle")
Object { get: strokeStyle(), set: strokeStyle(), enumerable: true, configurable: true }
(如果您有兴趣了解有关此模式的更多信息,请查看我在 JSON.parse not erroring on cyclic objects 上的回答。)
这里的问题是提供给 CanvasRenderingContext2DPrototype.strokeStyle
setter 的 this
是 proxy
对象,而不是实际的 ctx
对象。也就是说,当我们只在代理上设置一个 属性 时:
proxy.isAFake = true;
并在重新定义的 setter:
中测试它Object.defineProperty(ctx.__proto__, "strokeStyle", {
set: function() {
console.log("strokeStyle setter called for proxy?", this.isAFake);
}
});
我们看到 setter 记录仅代理 属性:strokeStyle setter called for proxy? true
。
无论出于何种原因,CanvasRenderingContext2DPrototype.strokeStyle
上的 setter 将只接受真正的 CanvasRenderingContext2D
实例,而不是代理实例。