考虑以下网页。
<html>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px solid #000000;">
</canvas>
</body>
</html>
我在Firefox中打开此页面,打开JS控制台并输入以下内容。
> document.getElementById("canvas").getContext("2d")
输出如下:
CanvasRenderingContext2D { canvas: canvas#canvas, mozCurrentTransform: (6) […], mozCurrentTransformInverse: (6) […], mozTextStyle: "10px sans-serif", mozImageSmoothingEnabled: true, globalAlpha: 1, globalCompositeOperation: "source-over", strokeStyle: "#000000", fillStyle: "#000000", filter: "none" }
另一方面,如果我自己创建一个对象并复制CanvasRenderingContext2D
的所有内容,它仍然只是一个普通对象。
var realContext = document.getElementById("canvas").getContext("2d")
var myContext = new Object()
for (var property in realContext) {
myContext[property] = realContext[property];
}
myContext
Object { drawImage: drawImage(), beginPath: beginPath(), fill: fill(), stroke: stroke(), clip: clip(), isPointInPath: isPointInPath(), isPointInStroke: isPointInStroke(), createLinearGradient: createLinearGradient(), createRadialGradient: createRadialGradient(), createPattern: createPattern(), … }
是什么使CanvasRenderingContext2D成为CanvasRenderingContext2D?
作为推论,我该如何将普通的旧对象变成 CanvasRenderingContext2D
?
编辑:我不在乎JS控制台的内容。我确实担心不能像使用原始上下文那样使用新上下文。
myContext.save()
TypeError: 'save' called on an object that does not implement interface CanvasRenderingContext2D
目标是能够像旧对象一样使用新对象并在原始画布上绘制。
编辑:我一直在寻找不需要 使用画布修改网站源代码的解决方案。
答案 0 :(得分:2)
这是一个摘要,它将开始记录对任何CanvasRenderingContext2D
的所有访问。我禁用了堆栈代码段,因为它在尝试序列化console.log()
输出时会引发很多错误,因此只需检查真实控制台的实际输出即可。
// handler used for intercepting proxy
const handler = {
get (target, key) {
// forward access to underlying property
const value = Reflect.get(target, key)
console.log('get', target, key, value)
// return underlying value as an intercepting proxy
// if value is function, its calling context will be the target
return proxyValue(target, value)
},
set (target, key, value) {
// forward access to underlying property
Reflect.set(target, key, value)
console.log('set', target, key, value)
// return set value as an intercepting proxy
// if value is function, it will not have a calling context
return proxyValue(undefined, value)
},
apply (target, thisArg, args) {
// forward invocation to underlying function
const value = Reflect.apply(target, thisArg, args)
console.log('apply', thisArg, target, args, value)
// return underlying return value as an intercepting proxy
// if value is a function, it will not have a calling context
return proxyValue(undefined, value)
}
}
// wrap all accessed non-primitives in an intercepting proxy
function proxyValue (target, value) {
switch (typeof value) {
case 'function':
// return function bound to target as proxy
return new Proxy(value.bind(target), handler)
case 'object':
// return object as proxy
return new Proxy(value, handler)
default:
// return primitive as normal
return value
}
}
// iterate all descriptors of CanvasRenderingContext2D prototype
const descriptors = Object.getOwnPropertyDescriptors(CanvasRenderingContext2D.prototype)
Object.entries(descriptors).forEach(([key, { value, get, set, configurable, enumerable, writable }]) => {
// if this is an accessor property
const accessor = get || set ? {
get () {
// forward access to underlying getter
const getValue = Reflect.apply(get, this, [])
console.log('get', this, key, getValue)
// return underlying value as an intercepting proxy
return proxyValue(this, getValue)
},
set (setValue) {
// forward access to underlying setter
Reflect.apply(set, this, [setValue])
console.log('set', this, key, setValue)
// return set value as an intercepting proxy
return proxyValue(this, setValue)
},
configurable,
enumerable
} : {
get () {
console.log('get', this, key, value)
// return underlying value as an intercepting proxy
return proxyValue(this, value)
},
set (setValue) {
// assign and return underlying value
value = setValue
console.log('set', this, key, setValue)
// return set value as an intercepting proxy
return proxyValue(this, setValue)
},
configurable,
enumerable
}
// overwrite property with intercepting accessor property
Object.defineProperty(CanvasRenderingContext2D.prototype, key, accessor)
})
const canvas = document.querySelector('canvas')
const context = canvas.getContext('2d')
context.fillStyle = 'red'
context.save()
context.getImageData(0, 0, 100, 100).data
<canvas></canvas>