渲染前保存 canvas 的数据图像
Save dataImage of canvas before rendering
我正在开发一个有两个 canvases 的项目。第一个 canvas 支持缩放和绘图,第二个 canvas 是第一个 canvas 的副本,它应该显示第一个 canvas 的一般视图而不缩放。我已经问过这个 , and this what I have now. In this solution, I copy the state of the canvas before scaling using function toDataURL
. But this is a very slow way and I get poor performance. I tried using getImageData
instead and got not what I expected. Also i tried to play with functions after:render
and before:render
, but without result, please, look this。我做错了什么?
1) 如果 devicePixelRatio > 1,Fabric.js 将其 canvas 放大。这就是为什么当您绘制从 getImageData()
。更好的方法是使用 drawImage()
并为其提供目标 width/height,以便您的浏览器决定是否应根据源图像尺寸缩小或放大这些像素。
2) 重置视口变换是正确的,但这还不够——您应该应用这个新变换并以某种方式重新渲染 canvas。这就是当您调用 toDataURL()
时 Fabric.js 所做的事情 - 它调用 toCanvasElement()
,它制作 canvas 的副本并在其上呈现所有对象。您的实施应反映此逻辑。
下面的解决方案介绍了drawOnCopyCanvas()
方法,是toCanvasElement()
的补丁版本。它不使用中间 canvas 而是直接在提供的 canvas 上绘制。
fabric.StaticCanvas.prototype.drawCopyOnCanvas = function(canvasEl) {
// save values
var scaledWidth = this.width,
scaledHeight = this.height,
vp = this.viewportTransform,
originalInteractive = this.interactive,
newVp = [1, 0, 0, 1, 0, 0],
originalRetina = this.enableRetinaScaling,
originalContextTop = this.contextTop;
// reset
this.contextTop = null;
this.enableRetinaScaling = false;
this.interactive = false;
this.viewportTransform = newVp;
this.calcViewportBoundaries();
// draw on copy
this.renderCanvas(canvasEl.getContext('2d'), this._objects);
// restore values
this.viewportTransform = vp;
this.calcViewportBoundaries();
this.interactive = originalInteractive;
this.enableRetinaScaling = originalRetina;
this.contextTop = originalContextTop;
}
function afterRender() {
// remove 'after:render' listener as canvas.toCanvasElement()
// calls renderCanvas(), which results in an infinite recursion
canvas.off('after:render', afterRender);
// draw c1 contents on c2
canvas.drawCopyOnCanvas(c2);
setTimeout(() => {
// re-attach the listener in the next event loop
canvas.on('after:render', afterRender);
});
}
canvas.on('after:render', afterRender);
我正在开发一个有两个 canvases 的项目。第一个 canvas 支持缩放和绘图,第二个 canvas 是第一个 canvas 的副本,它应该显示第一个 canvas 的一般视图而不缩放。我已经问过这个 toDataURL
. But this is a very slow way and I get poor performance. I tried using getImageData
instead and got not what I expected. Also i tried to play with functions after:render
and before:render
, but without result, please, look this。我做错了什么?
1) 如果 devicePixelRatio > 1,Fabric.js 将其 canvas 放大。这就是为什么当您绘制从 getImageData()
。更好的方法是使用 drawImage()
并为其提供目标 width/height,以便您的浏览器决定是否应根据源图像尺寸缩小或放大这些像素。
2) 重置视口变换是正确的,但这还不够——您应该应用这个新变换并以某种方式重新渲染 canvas。这就是当您调用 toDataURL()
时 Fabric.js 所做的事情 - 它调用 toCanvasElement()
,它制作 canvas 的副本并在其上呈现所有对象。您的实施应反映此逻辑。
下面的解决方案介绍了drawOnCopyCanvas()
方法,是toCanvasElement()
的补丁版本。它不使用中间 canvas 而是直接在提供的 canvas 上绘制。
fabric.StaticCanvas.prototype.drawCopyOnCanvas = function(canvasEl) {
// save values
var scaledWidth = this.width,
scaledHeight = this.height,
vp = this.viewportTransform,
originalInteractive = this.interactive,
newVp = [1, 0, 0, 1, 0, 0],
originalRetina = this.enableRetinaScaling,
originalContextTop = this.contextTop;
// reset
this.contextTop = null;
this.enableRetinaScaling = false;
this.interactive = false;
this.viewportTransform = newVp;
this.calcViewportBoundaries();
// draw on copy
this.renderCanvas(canvasEl.getContext('2d'), this._objects);
// restore values
this.viewportTransform = vp;
this.calcViewportBoundaries();
this.interactive = originalInteractive;
this.enableRetinaScaling = originalRetina;
this.contextTop = originalContextTop;
}
function afterRender() {
// remove 'after:render' listener as canvas.toCanvasElement()
// calls renderCanvas(), which results in an infinite recursion
canvas.off('after:render', afterRender);
// draw c1 contents on c2
canvas.drawCopyOnCanvas(c2);
setTimeout(() => {
// re-attach the listener in the next event loop
canvas.on('after:render', afterRender);
});
}
canvas.on('after:render', afterRender);