问题
Consider the following web page.
<html>
<body>
<canvas id="canvas" width="300" height="300" style="border:1px solid #000000;">
</canvas>
</body>
</html>
I open this page in Firefox, open the JS console and type the following.
> document.getElementById("canvas").getContext("2d")
The output is as follows:
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" }
On the other hand, if I create an object myself and copy all of the guts of the CanvasRenderingContext2D
, it's still just a plain object.
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(), … }
What makes a CanvasRenderingContext2D a CanvasRenderingContext2D?
As a corollary, how can I turn my plain old object into a CanvasRenderingContext2D
?
Edit: I don't care about what the JS console says. I do care that I cannot use my new context the same way I use the original one.
myContext.save()
TypeError: 'save' called on an object that does not implement interface CanvasRenderingContext2D
The goal is to be able to use the new object exactly like the old one and draw on the original canvas.
Edit: I've looked for solutions which do not require modification of the source code of the website using canvas.
回答1:
Here's a snippet that will start logging all accesses to any CanvasRenderingContext2D
. I disabled the stack snippet console because it throws some errors trying to serialize the console.log()
output, so just check the developer console for the output using F12.
function intercept(value) {
switch (typeof value) {
case 'function':
case 'object':
return new Proxy(value, {
get(target, key) {
const value = Reflect.get(target, key);
console.log('handler.get', target, key, value);
return intercept(value);
},
set(target, key, value) {
const result = Reflect.set(target, key, value);
console.log('handler.set', target, key, value);
return result;
},
apply(target, thisArg, args) {
const value = Reflect.apply(target, thisArg, args);
console.log('handler.apply', thisArg, target, ...args, value);
return intercept(value);
}
});
default:
return value;
}
}
const ownPropertyDescriptorEntries = Object.entries(
Object.getOwnPropertyDescriptors(
CanvasRenderingContext2D.prototype
)
);
const interceptedPropertyDescriptorEntries = ownPropertyDescriptorEntries.map(
([key, { get, set, value, ...descriptor }]) => [
key, get || set ? {
...descriptor,
get() {
console.log('descriptor.get', this, key);
return intercept(Reflect.apply(get, this, []));
},
set(value) {
console.log('descriptor.set', this, key, value);
return intercept(Reflect.apply(set, this, [value]));
}
} : {
...descriptor,
value: intercept(value)
}
]
);
Object.defineProperties(
CanvasRenderingContext2D.prototype,
Object.fromEntries(
interceptedPropertyDescriptorEntries
)
);
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
console.log(context.lineWidth);
context.fillStyle = 'red';
context.getImageData(0, 0, 100, 100).data[0] = 255;
<canvas></canvas>
descriptor.get CanvasRenderingContext2D lineWidth
1
descriptor.set CanvasRenderingContext2D fillStyle red
handler.apply CanvasRenderingContext2D ƒ getImageData() { [native code] } 0 0 100 100 ImageData
handler.get ImageData data Uint8ClampedArray(40000)
handler.set Uint8ClampedArray(40000) 0 255
References
- Proxy
- handler.get
- handler.set
- handler.apply
- Reflect.get()
- Reflect.set()
- Reflect.apply()
- Object.getOwnPropertyDescriptors()
- Object.defineProperty()
来源:https://stackoverflow.com/questions/52303490/what-makes-a-canvasrenderingcontext2d-a-canvasrenderingcontext2d