如何在不放大 fabricjs 的情况下显示另一个 canvas
How to display another canvas without zoom in fabricjs
简而言之,我正在开发一个项目,其中有两个 canvases:一个 canvas 支持缩放和绘图,第二个 canvas - 第一个的副本canvas,应该显示没有缩放的一般视图。不幸的是,我不明白如何实现这一点,因为第二个 canvas 也显示带有缩放的图片。请帮忙!
这是我所拥有的一个小例子:
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx1 = c1.getContext("2d");
var ctx2 = c2.getContext("2d");
function copy() {
var imgData = ctx1.getImageData(0, 0, 512, 512);
ctx2.putImageData(imgData, 0, 0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(() => {
copy();
}, 10)
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>
您的问题是您正在从上下文
中获取 "ImageData"
但你应该使用 fabric.Canvas
对象...
上下文只给你你所看到的,所以这两个 canvas 将是一个镜像,你必须从 fabric.Canvas
中获取数据,也就是具有 "big picture" 所有编辑。
在我的原型中,我使用 canvas.toJSON
获取数据,然后绘制我使用 loadFromJSON
的数据,但我确实注意到加载新图像时有些闪烁,所以我决定使用一个图像而不是静态 canvas,这样过渡是无缝的
请参阅下面我的简单原型:
var c1 = document.getElementById("scale");
var static = document.getElementById("img");
var canvas = new fabric.Canvas(c1, {isDrawingMode: true});
function copy() {
var data = canvas.toObject();
var c = new fabric.Canvas();
c.loadFromJSON(data, function() {
static.src = c.toDataURL({ format: 'png' });
});
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
}, { crossOrigin: "anonymous" }
);
canvas.on('mouse:wheel', function(opt) {
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom() + opt.e.deltaY / 200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({
x: opt.e.offsetX,
y: opt.e.offsetY
}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(copy, 200)
canvas {
border: 2px solid black;
}
img {
border: 2px solid red;
position: absolute;
top: 5px; right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js">
</script>
<canvas id="scale" width="300" height="150"></canvas>
<br><img id="img" width="300" height="150">
除非我们遇到一些浏览器兼容性问题(我只测试了 chrome),否则结果应该如下所示:
没有完美的解决方案,因为fabricJS不支持在更多目标上渲染。
这里有一个 toCanvasElement 方法可以创建另一个 canvas 的副本,但它不允许您指定要绘制的 canvas。
所以我在这里做的是,在某个时候:
- 将缩放重置为 1
- 渲染到新的 canvas
- 将缩放恢复到原来的状态
- 在静态 canvas
上复制 canvas
有一个额外的副本,在现代硬件上速度很快,以至于您不在乎,但最好向该 toCanvasElement 方法添加一个 canvas 参数以获取绘图在指定的 canvas 立即发生
除此之外,还有关于何时发射该副本的问题。
'after:render' 不是个好主意,它会永远循环,还会在缩放更改时重绘,这不是你想要的。
现在我使用 object:added,你也可以添加 object:modified,也许还有其他东西,但理想情况下,当你 运行 一些代码时,你会在需要时手动调用副本更改一些绘图属性。
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx2 = c2.getContext("2d");
function copy(copiedCanvas) {
ctx2.drawImage(copiedCanvas,0,0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
function afterRender() {
var originalVP = canvas.viewportTransform;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
copy(canvas.toCanvasElement());
canvas.viewportTransform = originalVP;
}
canvas.on('object:added', afterRender);
canvas.on('object:modified', afterRender);
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>
简而言之,我正在开发一个项目,其中有两个 canvases:一个 canvas 支持缩放和绘图,第二个 canvas - 第一个的副本canvas,应该显示没有缩放的一般视图。不幸的是,我不明白如何实现这一点,因为第二个 canvas 也显示带有缩放的图片。请帮忙! 这是我所拥有的一个小例子:
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx1 = c1.getContext("2d");
var ctx2 = c2.getContext("2d");
function copy() {
var imgData = ctx1.getImageData(0, 0, 512, 512);
ctx2.putImageData(imgData, 0, 0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(() => {
copy();
}, 10)
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>
您的问题是您正在从上下文
中获取 "ImageData"
但你应该使用 fabric.Canvas
对象...
上下文只给你你所看到的,所以这两个 canvas 将是一个镜像,你必须从 fabric.Canvas
中获取数据,也就是具有 "big picture" 所有编辑。
在我的原型中,我使用 canvas.toJSON
获取数据,然后绘制我使用 loadFromJSON
的数据,但我确实注意到加载新图像时有些闪烁,所以我决定使用一个图像而不是静态 canvas,这样过渡是无缝的
请参阅下面我的简单原型:
var c1 = document.getElementById("scale");
var static = document.getElementById("img");
var canvas = new fabric.Canvas(c1, {isDrawingMode: true});
function copy() {
var data = canvas.toObject();
var c = new fabric.Canvas();
c.loadFromJSON(data, function() {
static.src = c.toDataURL({ format: 'png' });
});
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
}, { crossOrigin: "anonymous" }
);
canvas.on('mouse:wheel', function(opt) {
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom() + opt.e.deltaY / 200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({
x: opt.e.offsetX,
y: opt.e.offsetY
}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
setInterval(copy, 200)
canvas {
border: 2px solid black;
}
img {
border: 2px solid red;
position: absolute;
top: 5px; right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js">
</script>
<canvas id="scale" width="300" height="150"></canvas>
<br><img id="img" width="300" height="150">
除非我们遇到一些浏览器兼容性问题(我只测试了 chrome),否则结果应该如下所示:
没有完美的解决方案,因为fabricJS不支持在更多目标上渲染。
这里有一个 toCanvasElement 方法可以创建另一个 canvas 的副本,但它不允许您指定要绘制的 canvas。
所以我在这里做的是,在某个时候:
- 将缩放重置为 1
- 渲染到新的 canvas
- 将缩放恢复到原来的状态
- 在静态 canvas 上复制 canvas
有一个额外的副本,在现代硬件上速度很快,以至于您不在乎,但最好向该 toCanvasElement 方法添加一个 canvas 参数以获取绘图在指定的 canvas 立即发生
除此之外,还有关于何时发射该副本的问题。 'after:render' 不是个好主意,它会永远循环,还会在缩放更改时重绘,这不是你想要的。
现在我使用 object:added,你也可以添加 object:modified,也许还有其他东西,但理想情况下,当你 运行 一些代码时,你会在需要时手动调用副本更改一些绘图属性。
var c1 = document.getElementById("scale");
var c2 = document.getElementById("static");
var canvas = this.__canvas = new fabric.Canvas(c1, {
isDrawingMode: true
});
var ctx2 = c2.getContext("2d");
function copy(copiedCanvas) {
ctx2.drawImage(copiedCanvas,0,0);
}
fabric.Image.fromURL(
"https://cdn-images-1.medium.com/max/1800/1*sg-uLNm73whmdOgKlrQdZA.jpeg",
img => {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
},
{
crossOrigin: "Annoymous"
}
);
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom + delta/200;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
function afterRender() {
var originalVP = canvas.viewportTransform;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
copy(canvas.toCanvasElement());
canvas.viewportTransform = originalVP;
}
canvas.on('object:added', afterRender);
canvas.on('object:modified', afterRender);
canvas {
border: 1px solid black;
}
#static {
position: relative;
top: -300px;
left: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.1/fabric.min.js"> </script>
This is interactive canvas
<canvas id="scale" width="300" height="300"></canvas>
<canvas id="static"width="300" height="300"></canvas>