FabricJS:在两个实例之间平移/缩放后同步对象的位置



Fabric JS问题。

屏幕截图这就是我的Fabric JS应用程序的样子

CODEPENhttps://codepen.io/zaynbuksh/pen/VVKRxj?editors=0011(alt点击拖动以平移,滚轮缩放(

TLDR平移和缩放几次后,如何使线条保持不变?


我正在开发一个用于标记样本图像的Fabric JS应用程序。作为这项工作的一部分,人们希望能够放大每个标签所指向的内容。我被要求在放大样本图像时使标签保持可见。根据研究,人们建议将两张画布叠放在一起。

我创建了两个FabricJS画布实例,它们层叠在一起。底部的画布包含一个可以缩放和平移的背景图像,其上方的画布显示一条未缩放的指针线/标签(以保持标签始终可见(。

起初一切都正常-当我平移和缩放第一次时,线条与图像保持同步。在那之后,我在将行同步到图像时遇到了问题。

当我平移然后缩放两次或更多次时,问题就会显现出来每次平移和缩放时问题都会重复,即缩放时线会移动,但平移时会保持同步,再次缩放时会再次移动,正常平移等等…


(平移由alt点击拖动处理,缩放由滚轮处理(

/*
"mouse:wheel" event is where zooms are handled
"mouse:move" event is where panning is handled
*/
// create Fabric JS canvas'
var labelsCanvas = new fabric.Canvas("labelsCanvas");
var specimenCanvas = new fabric.Canvas("specimenCanvas");
//set defaults
var startingPositionForLine = 100;
const noZoom = 1;
var wasPanned = false;
var panY2 = startingPositionForLine;
var panX2 = startingPositionForLine;
var zoomY2 = startingPositionForLine;
var zoomX2 = startingPositionForLine;
// set starting zoom for specimen canvas
var specimenZoom = noZoom;
/* 
Add pointer, label and background image into canvas
*/
// create a pointer line
var line = new fabric.Line([150, 35, panX2, panY2], {
fill: "red",
stroke: "red",
strokeWidth: 3,
strokeDashArray: [5, 2],
// selectable: false,
evented: false
});
// create text label
var text = new fabric.Text("Label 1", {
left: 100,
top: 0,
// selectable: false,
evented: false,
backgroundColor: "red"
});
// add both into "Labels" canvas
labelsCanvas.add(text);
labelsCanvas.add(line);
// add a background image into Specimen canvas
fabric.Image.fromURL(
"https://upload.wikimedia.org/wikipedia/commons/c/cb/Skull_brain_human_normal.svg",
function(oImg) {
oImg.left = 0;
oImg.top = 0;
oImg.scaleToWidth(300);
oImg.scaleToHeight(300);
specimenCanvas.add(oImg);
}
);
/* 
Handle mouse events
*/
// zoom the specimen image canvas via a mouse scroll-wheel event
labelsCanvas.on("mouse:wheel", function(opt) {
// scroll value e.g. 5, 6 -1, -18
var delta = opt.e.deltaY;
// zoom level in specimen
var zoom = specimenCanvas.getZoom();
console.log("zoom ", zoom);
// make zoom smaller
zoom = zoom + delta / 200;
// use sane defaults for zoom
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
// create new zoom value
zoomX2 = panX2 * zoom;
zoomY2 = panY2 * zoom;
// save the zoom
specimenZoom = zoom;
// set the specimen canvas zoom
specimenCanvas.setZoom(zoom);
// move line to sync it with the zoomed image
line.set({
x2: zoomX2,
y2: zoomY2
});
console.log("zoomed line ", line.x2);
// render the changes
this.requestRenderAll();
// block default mouse behaviour
opt.e.preventDefault();
opt.e.stopPropagation();
console.log(labelsCanvas.viewportTransform[4]);
// stuff I've tried to fix errors
line.setCoords();
specimenCanvas.calcOffset();
});
// pan the canvas
labelsCanvas.on("mouse:move", function(opt) {
if (this.isDragging) {

// pick up the click and drag event
var e = opt.e;

// sync the label position with the panning
text.left = text.left + (e.clientX - this.lastPosX);
var x2ToUse;
var y2ToUse;
// UNZOOMED canvas is being panned
if (specimenZoom === noZoom) {
x2ToUse = panX2;
y2ToUse = panY2;

// move the image using the difference between 
// the current position and last known position 
line.set({
x1: line.x1 + (e.clientX - this.lastPosX),
y1: line.y1,
x2: x2ToUse + (e.clientX - this.lastPosX),
y2: y2ToUse + (e.clientY - this.lastPosY)
});

// set the new panning value
panX2 = line.x2;
panY2 = line.y2;

// stuff I've tried
// zoomX2 = line.x2;
// zoomY2 = line.y2;
} 

// ZOOMED canvas is being panned
else 
{
x2ToUse = zoomX2;
y2ToUse = zoomY2;

// stuff I've tried
// x2ToUse = panX2;
// y2ToUse = panY2;
// move the image using the difference between 
// the current position and last known ZOOMED position 
line.set({
x1: line.x1 + (e.clientX - this.lastPosX),
y1: line.y1,
x2: x2ToUse + (e.clientX - this.lastPosX),
y2: y2ToUse + (e.clientY - this.lastPosY)
});

zoomX2 = line.x2;
zoomY2 = line.y2;
}
// hide label/pointer when it is out of view
if (text.left < 0 || line.y2 < 35) {
text.animate("opacity", "0", {
duration: 15,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
line.animate("opacity", "0", {
duration: 15,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
} 
// show label/pointer when it is in view
else 
{
text.animate("opacity", "1", {
duration: 25,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
line.animate("opacity", "1", {
duration: 25,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
}
specimenCanvas.viewportTransform[4] += e.clientX - this.lastPosX;
specimenCanvas.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
specimenCanvas.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
console.log(line.x2);
wasPanned = true;
});
labelsCanvas.on("mouse:down", function(opt) {
var evt = opt.e;
if (evt.altKey === true) {
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
});
labelsCanvas.on("mouse:up", function(opt) {
this.isDragging = false;
this.selection = true;
});
.canvas-container {
position: absolute!important;
left: 0!important;
top: 0!important;
}
.canvas {
position: absolute;
top: 0;
right: 0;
border: solid red 1px;
}
.label-canvas {
z-index: 2;
}
.specimen-canvas {
z-index: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<h1>
Dual canvas test
</h1>
<div style="position: relative; height: 300px">
<canvas class="canvas specimen-canvas" id="specimenCanvas" width="300" height="300"></canvas>
<canvas class="canvas label-canvas" id="labelsCanvas" width="300" height="300"></canvas>
</div>

顺便说一句,我认为您有点过于复杂了。您实际上不需要分别存储panX/panYzoomX/zoomY(如我所猜测的那样缩放(,它们已经在line的坐标中了。只是说,因为它们可能导致了混乱/调试。然而,修复的核心思想是,应该将line的坐标乘以newZoom / previousZoom的比例,而不是整个缩放值。我已经更新了你的片段,它似乎如预期的那样工作:

/*
"mouse:wheel" event is where zooms are handled
"mouse:move" event is where panning is handled
*/
// create Fabric JS canvas'
var labelsCanvas = new fabric.Canvas("labelsCanvas");
var specimenCanvas = new fabric.Canvas("specimenCanvas");
//set defaults
var startingPositionForLine = 100;
const noZoom = 1;
var wasPanned = false;
var panY2 = startingPositionForLine;
var panX2 = startingPositionForLine;
var zoomY2 = startingPositionForLine;
var zoomX2 = startingPositionForLine;
// set starting zoom for specimen canvas
var specimenZoom = noZoom;
var prevZoom = noZoom;
/* 
Add pointer, label and background image into canvas
*/
// create a pointer line
var line = new fabric.Line([150, 35, panX2, panY2], {
fill: "red",
stroke: "red",
strokeWidth: 3,
strokeDashArray: [5, 2],
// selectable: false,
evented: false
});
// create text label
var text = new fabric.Text("Label 1", {
left: 100,
top: 0,
// selectable: false,
evented: false,
backgroundColor: "red"
});
// add both into "Labels" canvas
labelsCanvas.add(text);
labelsCanvas.add(line);
// add a background image into Specimen canvas
fabric.Image.fromURL(
"https://upload.wikimedia.org/wikipedia/commons/c/cb/Skull_brain_human_normal.svg",
function(oImg) {
oImg.left = 0;
oImg.top = 0;
oImg.scaleToWidth(300);
oImg.scaleToHeight(300);
specimenCanvas.add(oImg);
}
);
window.specimenCanvas = specimenCanvas
/* 
Handle mouse events
*/
// zoom the specimen image canvas via a mouse scroll-wheel event
labelsCanvas.on("mouse:wheel", function(opt) {
// scroll value e.g. 5, 6 -1, -18
var delta = opt.e.deltaY;
// zoom level in specimen
var zoom = specimenCanvas.getZoom();
var lastZoom = zoom
// make zoom smaller
zoom = zoom + delta / 200;
// use sane defaults for zoom
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
// save the zoom
specimenZoom = zoom;
// set the specimen canvas zoom
specimenCanvas.setZoom(zoom);
// move line to sync it with the zoomed image
var zoomRatio = zoom / lastZoom
console.log('zoom ratio: ', zoomRatio)
line.set({
x2: line.x2 * zoomRatio,
y2: line.y2 * zoomRatio
});

// console.log("zoomed line ", line.x2);
// render the changes
this.requestRenderAll();
// block default mouse behaviour
opt.e.preventDefault();
opt.e.stopPropagation();
// console.log(labelsCanvas.viewportTransform[4]);
// stuff I've tried to fix errors
line.setCoords();
specimenCanvas.calcOffset();
});
// pan the canvas
labelsCanvas.on("mouse:move", function(opt) {
if (this.isDragging) {

// pick up the click and drag event
var e = opt.e;

// sync the label position with the panning
text.left = text.left + (e.clientX - this.lastPosX);
// UNZOOMED canvas is being panned
if (specimenZoom === noZoom) {
x2ToUse = panX2;
y2ToUse = panY2;

// move the image using the difference between 
// the current position and last known position 
line.set({
x1: line.x1 + (e.clientX - this.lastPosX),
y1: line.y1,
x2: line.x2 + (e.clientX - this.lastPosX),
y2: line.y2 + (e.clientY - this.lastPosY)
});

// stuff I've tried
// zoomX2 = line.x2;
// zoomY2 = line.y2;
} 

// ZOOMED canvas is being panned
else 
{
// move the image using the difference between 
// the current position and last known ZOOMED position 
line.set({
x1: line.x1 + (e.clientX - this.lastPosX),
y1: line.y1,
x2: line.x2 + (e.clientX - this.lastPosX),
y2: line.y2 + (e.clientY - this.lastPosY)
});
}
// hide label/pointer when it is out of view
if (text.left < 0 || line.y2 < 35) {
text.animate("opacity", "0", {
duration: 15,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
line.animate("opacity", "0", {
duration: 15,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
} 
// show label/pointer when it is in view
else 
{
text.animate("opacity", "1", {
duration: 25,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
line.animate("opacity", "1", {
duration: 25,
onChange: labelsCanvas.renderAll.bind(labelsCanvas)
});
}
specimenCanvas.viewportTransform[4] += e.clientX - this.lastPosX;
specimenCanvas.viewportTransform[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
specimenCanvas.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
prevZoom = specimenCanvas.getZoom()
}
// console.log(line.x2);
wasPanned = true;
});
labelsCanvas.on("mouse:down", function(opt) {
var evt = opt.e;
if (evt.altKey === true) {
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
});
labelsCanvas.on("mouse:up", function(opt) {
this.isDragging = false;
this.selection = true;
});
.canvas-container {
position: absolute!important;
left: 0!important;
top: 0!important;
}
.canvas {
position: absolute;
top: 0;
right: 0;
border: solid red 1px;
}
.label-canvas {
z-index: 2;
}
.specimen-canvas {
z-index: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<h1>
Dual canvas test
</h1>
<div style="position: relative; height: 300px">
<canvas class="canvas specimen-canvas" id="specimenCanvas" width="300" height="300"></canvas>
<canvas class="canvas label-canvas" id="labelsCanvas" width="300" height="300"></canvas>
</div>

最新更新