可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How can I pan and zoom using fabricjs? I've tried using the methods zoomToPoint and setZoom but they do not work for panning. Once I start using different zoom points I start having trouble.
$('#zoomIn').click(function(){ canvas.setZoom(canvas.getZoom() * 1.1 ) ; }) ; $('#zoomOut').click(function(){ canvas.setZoom(canvas.getZoom() / 1.1 ) ; }) ; $('#goRight').click(function(){ //Need to implement }) ; $('#goLeft').click(function(){ //Need to implement }) ;
http://jsfiddle.net/hdramos/ux16013L/
回答1:
Solved it using:
relativePan() absolutePan()
[Update]
$('#goRight').click(function(){ var units = 10 ; var delta = new fabric.Point(units,0) ; canvas.relativePan(delta) ; }) ; $('#goLeft').click(function(){ var units = 10 ; var delta = new fabric.Point(-units,0) ; canvas.relativePan(delta) ; }) ; $('#goUp').click(function(){ var units = 10 ; var delta = new fabric.Point(0,-units) ; canvas.relativePan(delta) ; }) ; $('#goDown').click(function(){ var units = 10 ; var delta = new fabric.Point(0,units) ; canvas.relativePan(delta) ; });
http://jsfiddle.net/ux16013L/2/
回答2:
I know it is already answered, but I had to do a mouse panning, so I adapted the fiddle of the accepted answer to do so. I post it here for anyone who has to do something like this. This is just the main idea:
var panning = false; canvas.on('mouse:up', function (e) { panning = false; }); canvas.on('mouse:down', function (e) { panning = true; }); canvas.on('mouse:move', function (e) { if (panning && e && e.e) { var units = 10; var delta = new fabric.Point(e.e.movementX, e.e.movementY); canvas.relativePan(delta); } });
Here is the fiddle: http://jsfiddle.net/gncabrera/hkee5L6d/5/
回答3:
Here is my solution for canvas zoom (using mouse wheel) and pan (using left/up/right /down keys or shift key + mouse left down + mouse move).
https://jsfiddle.net/milanhlinak/7s4w0uLy/8/
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="lib/jquery-3.1.1.min.js"></script> <script type="text/javascript" src="lib/fabric.min.js"></script> </head> <body> <canvas id="canvas" style="border: 1px solid #cccccc"></canvas> <script> var Direction = { LEFT: 0, UP: 1, RIGHT: 2, DOWN: 3 }; var zoomLevel = 0; var zoomLevelMin = 0; var zoomLevelMax = 3; var shiftKeyDown = false; var mouseDownPoint = null; var canvas = new fabric.Canvas('canvas', { width: 500, height: 500, selectionKey: 'ctrlKey' }); canvas.add(new fabric.Rect({ left: 100, top: 100, width: 50, height: 50, fill: '#faa' })); canvas.add(new fabric.Rect({ left: 300, top: 300, width: 50, height: 50, fill: '#afa' })); canvas.on('mouse:down', function (options) { var pointer = canvas.getPointer(options.e, true); mouseDownPoint = new fabric.Point(pointer.x, pointer.y); }); canvas.on('mouse:up', function (options) { mouseDownPoint = null; }); canvas.on('mouse:move', function (options) { if (shiftKeyDown && mouseDownPoint) { var pointer = canvas.getPointer(options.e, true); var mouseMovePoint = new fabric.Point(pointer.x, pointer.y); canvas.relativePan(mouseMovePoint.subtract(mouseDownPoint)); mouseDownPoint = mouseMovePoint; keepPositionInBounds(canvas); } }); fabric.util.addListener(document.body, 'keydown', function (options) { if (options.repeat) { return; } var key = options.which || options.keyCode; // key detection if (key == 16) { // handle Shift key canvas.defaultCursor = 'move'; canvas.selection = false; shiftKeyDown = true; } else if (key === 37) { // handle Left key move(Direction.LEFT); } else if (key === 38) { // handle Up key move(Direction.UP); } else if (key === 39) { // handle Right key move(Direction.RIGHT); } else if (key === 40) { // handle Down key move(Direction.DOWN); } }); fabric.util.addListener(document.body, 'keyup', function (options) { var key = options.which || options.keyCode; // key detection if (key == 16) { // handle Shift key canvas.defaultCursor = 'default'; canvas.selection = true; shiftKeyDown = false; } }); jQuery('.canvas-container').on('mousewheel', function (options) { var delta = options.originalEvent.wheelDelta; if (delta != 0) { var pointer = canvas.getPointer(options.e, true); var point = new fabric.Point(pointer.x, pointer.y); if (delta > 0) { zoomIn(point); } else if (delta < 0) { zoomOut(point); } } }); function move(direction) { switch (direction) { case Direction.LEFT: canvas.relativePan(new fabric.Point(-10 * canvas.getZoom(), 0)); break; case Direction.UP: canvas.relativePan(new fabric.Point(0, -10 * canvas.getZoom())); break; case Direction.RIGHT: canvas.relativePan(new fabric.Point(10 * canvas.getZoom(), 0)); break; case Direction.DOWN: canvas.relativePan(new fabric.Point(0, 10 * canvas.getZoom())); break; } keepPositionInBounds(canvas); } function zoomIn(point) { if (zoomLevel < zoomLevelMax) { zoomLevel++; canvas.zoomToPoint(point, Math.pow(2, zoomLevel)); keepPositionInBounds(canvas); } } function zoomOut(point) { if (zoomLevel > zoomLevelMin) { zoomLevel--; canvas.zoomToPoint(point, Math.pow(2, zoomLevel)); keepPositionInBounds(canvas); } } function keepPositionInBounds() { var zoom = canvas.getZoom(); var xMin = (2 - zoom) * canvas.getWidth() / 2; var xMax = zoom * canvas.getWidth() / 2; var yMin = (2 - zoom) * canvas.getHeight() / 2; var yMax = zoom * canvas.getHeight() / 2; var point = new fabric.Point(canvas.getWidth() / 2, canvas.getHeight() / 2); var center = fabric.util.transformPoint(point, canvas.viewportTransform); var clampedCenterX = clamp(center.x, xMin, xMax); var clampedCenterY = clamp(center.y, yMin, yMax); var diffX = clampedCenterX - center.x; var diffY = clampedCenterY - center.y; if (diffX != 0 || diffY != 0) { canvas.relativePan(new fabric.Point(diffX, diffY)); } } function clamp(value, min, max) { return Math.max(min, Math.min(value, max)); } </script> </body> </html>
回答4:
I have an example on Github using fabric.js Canvas panning and zooming: https://sabatinomasala.github.io/fabric-clipping-demo/
The code responsible for the panning behaviour is the following: https://github.com/SabatinoMasala/fabric-clipping-demo/blob/master/src/classes/Panning.js
It's a simple extension on the fabric.Canvas.prototype
, which enables you to toggle 'drag mode' on the canvas as follows:
canvas.toggleDragMode(true); // Start panning canvas.toggleDragMode(false); // Stop panning
Take a look at the following snippet, documentation is available throughout the code.
const STATE_IDLE = 'idle'; const STATE_PANNING = 'panning'; fabric.Canvas.prototype.toggleDragMode = function(dragMode) { // Remember the previous X and Y coordinates for delta calculations let lastClientX; let lastClientY; // Keep track of the state let state = STATE_IDLE; // We're entering dragmode if (dragMode) { // Discard any active object this.discardActiveObject(); // Set the cursor to 'move' this.defaultCursor = 'move'; // Loop over all objects and disable events / selectable. We remember its value in a temp variable stored on each object this.forEachObject(function(object) { object.prevEvented = object.evented; object.prevSelectable = object.selectable; object.evented = false; object.selectable = false; }); // Remove selection ability on the canvas this.selection = false; // When MouseUp fires, we set the state to idle this.on('mouse:up', function(e) { state = STATE_IDLE; }); // When MouseDown fires, we set the state to panning this.on('mouse:down', (e) => { state = STATE_PANNING; lastClientX = e.e.clientX; lastClientY = e.e.clientY; }); // When the mouse moves, and we're panning (mouse down), we continue this.on('mouse:move', (e) => { if (state === STATE_PANNING && e && e.e) { // let delta = new fabric.Point(e.e.movementX, e.e.movementY); // No Safari support for movementX and movementY // For cross-browser compatibility, I had to manually keep track of the delta // Calculate deltas let deltaX = 0; let deltaY = 0; if (lastClientX) { deltaX = e.e.clientX - lastClientX; } if (lastClientY) { deltaY = e.e.clientY - lastClientY; } // Update the last X and Y values lastClientX = e.e.clientX; lastClientY = e.e.clientY; let delta = new fabric.Point(deltaX, deltaY); this.relativePan(delta); this.trigger('moved'); } }); } else { // When we exit dragmode, we restore the previous values on all objects this.forEachObject(function(object) { object.evented = (object.prevEvented !== undefined) ? object.prevEvented : object.evented; object.selectable = (object.prevSelectable !== undefined) ? object.prevSelectable : object.selectable; }); // Reset the cursor this.defaultCursor = 'default'; // Remove the event listeners this.off('mouse:up'); this.off('mouse:down'); this.off('mouse:move'); // Restore selection ability on the canvas this.selection = true; } }; // Create the canvas let canvas = new fabric.Canvas('fabric') canvas.backgroundColor = '#f1f1f1'; // Add a couple of rects let rect = new fabric.Rect({ width: 100, height: 100, fill: '#f00' }); canvas.add(rect) rect = new fabric.Rect({ width: 200, height: 200, top: 200, left: 200, fill: '#f00' }); canvas.add(rect) // Handle dragmode change let dragMode = false; $('#dragmode').change(_ => { dragMode = !dragMode; canvas.toggleDragMode(dragMode); });
<div> <label for="dragmode"> Enable panning <input type="checkbox" id="dragmode" name="dragmode" /> </label> </div> <canvas width="300" height="300" id="fabric"></canvas> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.15/fabric.min.js"></script>
回答5:
If you just want to pan the canvas in the display, and not to change the position of the elements, you can use this solution.
The idea is to have a fixed size container with css of overflow: hidden
that has an expended canvas inside it. the pan will move the canvas inside the container so the user will see different areas of the expended canvas every time.