Warm tip: This article is reproduced from stackoverflow.com, please click
canvas fabricjs javascript

How to display another canvas without zoom in fabricjs

发布于 2020-04-13 09:19:25

In short, I am developing a project in which there are two canvases: a canvas that supports zoom and drawing, a second canvas - a duplicate of the first canvas, which should display a general view without zoom. Unfortunately, I do not understand how to implement this, since the second canvas also displays a picture with a zoom. Please, help! Here is a small example of what I have:

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>
Questioner
Arkadiy Vinkovskiy
Viewed
96
AndreaBogazzi 2020-02-02 19:58

There is not a perfect solution because fabricJS does not support rendering on more targets.

There is a toCanvasElement method here that creates a copy to another canvas, but it does not let you specify the canvas you want to draw on.

So here what i m doing is, at some point:

  • reset the zoom to 1
  • render to a new canvas
  • put back the zoom to what it was
  • copy that canvas on the static canvas

There is an extra copy, that is fast on modern hardware, to the point that you do not care, but would be nicer to add a canvas argument to that toCanvasElement method in order to get the drawing happening on the specified canvas immediately

Then apart from that there is the quesiton on when firing that copy. 'after:render' is not a great idea, it will loop forever and it will also redraw on zoom changes, that is not what you want.

For now i use object:added, you can add also object:modified, and maybe something else but ideally you will call a copy manually when needed when you run some code that will change some of the drawing properties.

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>