我正在开发一个项目,其中有两个 Canvas 。第一个 Canvas 支持缩放和绘图,第二个 Canvas 是第一个 Canvas 的副本,它应该显示第一个 Canvas 的一般 View ,无需缩放。我已经问过这个here ,这就是我 have现在。在此解决方案中,我在缩放之前使用函数 toDataURL
复制 Canvas 的状态。但这是一种非常慢的方法,而且性能很差。我尝试使用 getImageData
代替, got not what I expected 。我还尝试使用函数 after:render
和 before:render
,但没有结果,请查看 this 。我做错了什么?
最佳答案
1) Fabric.js 会放大其 Canvas ,如果 devicePixelRatio is > 1。这就是为什么当您绘制从 getImageData()
获得的像素时会得到“缩放”图片的原因。更好的方法是使用 drawImage()
并为其提供目标宽度/高度,以便浏览器决定是否应根据源图像尺寸缩小或放大这些像素。
2)您关于重置视口(viewport)变换的说法是正确的,但这还不够 - 您应该应用这个新的变换并以某种方式重新渲染 Canvas 。这就是 Fabric.js 在调用 toDataURL()
时所做的事情 - 它调用 toCanvasElement()
,这会创建 Canvas 的副本并在其上渲染所有对象。您的实现应该反射(reflect)此逻辑。
下面的解决方案引入了 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);
关于javascript - 渲染前保存canvas的dataImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60151384/