问题
I need to enable touch zoom/panning on a FabricJS canvas. There are libraries that will allow this behavior on an image (see pinch-zoom-canvas) or via mouse-click events (see this Fiddle) but I can't seem to get the 'touch:gesture' events hooked up properly.
I've built the library with gestures enabled (so this FabricJS demo works locally for me), but I don't know where to start with combining the gestures with the working fiddle.
I tried variations of code like this:
canvas.on({
'touch:gesture': function() {
var text = document.createTextNode(' Gesture ');
info.insertBefore(text, info.firstChild);
// Handle zoom only if 2 fingers are touching the screen
if (event.e.touches && event.e.touches.length == 2) {
// Get event point
var point = new fabric.Point(event.self.x, event.self.y);
// Remember canvas scale at gesture start
if (event.self.state == "start") {
zoomStartScale = self.canvas.getZoom();
}
// Calculate delta from start scale
var delta = zoomStartScale * event.self.scale;
// Zoom to pinch point
self.canvas.zoomToPoint(point, delta);
}
},
'touch:drag': function(e) {
panning = true;
var text = document.createTextNode(' Dragging ');
info.insertBefore(text, info.firstChild);
if (panning && e && e.e) {
debugger;
var units = 10;
var delta = new fabric.Point(e.e.movementX, e.e.movementY);
canvas.relativePan(delta);
}
panning = false;
},
'touch:longpress': function() {
var text = document.createTextNode(' Longpress ');
info.insertBefore(text, info.firstChild);
}
});
But nothing happens when I test on iPhone/iPad.
回答1:
The pinch to zoom was a stupid mistake, I hadn't included the event in the function parameter. The code below is working for pinch/zoom and tap/drag.
canvas.on({
'touch:gesture': function(e) {
if (e.e.touches && e.e.touches.length == 2) {
pausePanning = true;
var point = new fabric.Point(e.self.x, e.self.y);
if (e.self.state == "start") {
zoomStartScale = self.canvas.getZoom();
}
var delta = zoomStartScale * e.self.scale;
self.canvas.zoomToPoint(point, delta);
pausePanning = false;
}
},
'object:selected': function() {
pausePanning = true;
},
'selection:cleared': function() {
pausePanning = false;
},
'touch:drag': function(e) {
if (pausePanning == false && undefined != e.e.layerX && undefined != e.e.layerY) {
currentX = e.e.layerX;
currentY = e.e.layerY;
xChange = currentX - lastX;
yChange = currentY - lastY;
if( (Math.abs(currentX - lastX) <= 50) && (Math.abs(currentY - lastY) <= 50)) {
var delta = new fabric.Point(xChange, yChange);
canvas.relativePan(delta);
}
lastX = e.e.layerX;
lastY = e.e.layerY;
}
}
});
The absolute ~50px if/then statement is to avoid drags far away from the last point causing the canvas to jump. Also paused the panning to be able to move object independently. Pinch/zoom code was found in a github issues thread.
回答2:
I had some trouble getting the panning part of Laurens code to work on Fabric 2.3.3. So in case anyone encounters the same problem I wanted to share my solution.
My problem was, that I could not get panning to work on my smartphone, even though it worked on a pc. After some debugging I found that I can solve it by replacing e.e.layerX with e.self.x (and e.e.layerY with e.self.y).
Heres the full modified code:
canvas.on({
'touch:gesture': function(e) {
if (e.e.touches && e.e.touches.length == 2) {
pausePanning = true;
var point = new fabric.Point(e.self.x, e.self.y);
if (e.self.state == "start") {
zoomStartScale = canvas.getZoom();
}
var delta = zoomStartScale * e.self.scale;
canvas.zoomToPoint(point, delta);
pausePanning = false;
}
},
'object:selected': function() {
pausePanning = true;
},
'selection:cleared': function() {
pausePanning = false;
},
'touch:drag': function(e) {
if (pausePanning == false && undefined != e.self.x && undefined != e.self.x) {
currentX = e.self.x;
currentY = e.self.y;
xChange = currentX - lastX;
yChange = currentY - lastY;
if( (Math.abs(currentX - lastX) <= 50) && (Math.abs(currentY - lastY) <= 50)) {
var delta = new fabric.Point(xChange, yChange);
canvas.relativePan(delta);
}
lastX = e.self.x;
lastY = e.self.y;
}
}
});}
回答3:
I was able to achieve multi-touch panning and zooming with the mouse:wheel event.
When doing pinch to zoom - most browsers usually emit the ctrl button as well to distinguish between a scroll operation and not.
I then utilize the DeltaX and DeltaY properties of the Fabric event to either zoom or pan similar to the sample code they provided.
Every place that I call setState below should be a variable outside the function if you are not using with React.
this.canvas.on("mouse:wheel", opt => {
if (!this.canvas.viewportTransform) {
return;
}
var evt: any = opt.e;
if (evt.ctrlKey === true) {
var evt: any = opt.e;
var deltaY = evt.deltaY;
var zoom = this.canvas.getZoom();
zoom = zoom - deltaY / 100;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
this.canvas.zoomToPoint(
new fabric.Point(evt.offsetX, evt.offsetY),
zoom
);
} else {
console.log("Scrolling!!");
//inverted scrolling enabled. Remove the -1 multiplier to disable
this.canvas.viewportTransform[4] += (evt.deltaX)*-1;
this.canvas.viewportTransform[5] += (evt.deltaY)*-1;
this.canvas.requestRenderAll();
this.setState({ lastPosX: evt.clientX });
this.setState({ lastPosY: evt.clientY });
}
opt.e.preventDefault();
opt.e.stopPropagation();
});
来源:https://stackoverflow.com/questions/45110576/fabricjs-touch-pan-zoom-entire-canvas