如何在不放大 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>