The custom build mentioned by Ostkontentitan has not been updated in many years. Here is a way to pinch-zoom the canvas with later versions. I did this using Fabric.js 5.3.1:
Before I go into detail please be aware there are two ways you can do this, either by re-drawing the canvas content or by scaling the canvas using CSS transform: scale()
. The latter is vastly more performant.
I made a demo comparing drag and zoom using Fabric.js versus using CSS transform: https://codepen.io/Fjonan/pen/azoWXWJ
Since you requested it I will go into detail on how to do it using Fabric.js native functions although I recommend looking into CSS transform.
Setup something like this:
<canvas id=canvas>
</canvas>
For touch events we have to attach our own event listeners since Fabric.js does not (yet) support touch events as part of their handled listeners.
Fabric.js will create its own wrapper element canvas-container
which I access here using canvas.wrapperEl
.
const canvas = new fabric.Canvas("canvas",{
allowTouchScrolling: false,
// …
})
let pinchCenter,
initialDistance
canvas.wrapperEl.addEventListener('touchstart', pinchCanvasStart)
canvas.wrapperEl.addEventListener('touchmove', pinchCanvas)
/**
* Save the distance between the touch points when starting the pinch
*/
function pinchCanvasStart(event) {
if (event.touches.length !== 2) {
return
}
initialDistance = getPinchDistance(event.touches[0], event.touches[1])
}
/**
* Start pinch-zooming the canvas
*/
function pinchCanvas(event) {
if (event.touches.length !== 2) {
return
}
setPinchCenter(event.touches[0], event.touches[1])
const currentDistance = getPinchDistance(event.touches[0], event.touches[1])
let scale = (currentDistance / initialDistance).toFixed(2)
scale = 1 + (scale - 1) / 20 // slows down scale from pinch
zoomCanvas(scale * canvas.getZoom(), pinchCenter)
}
/**
* Zoom the canvas content using fabric JS
*/
function zoomCanvas(zoom, aroundPoint) {
canvas.zoomToPoint(aroundPoint, zoom)
canvas.renderAll()
}
/**
* Putting touch point coordinates into an object
*/
function getPinchCoordinates(touch1, touch2) {
return {
x1: touch1.clientX,
y1: touch1.clientY,
x2: touch2.clientX,
y2: touch2.clientY,
}
}
/**
* Returns the distance between two touch points
*/
function getPinchDistance(touch1, touch2) {
const coord = getPinchCoordinates(touch1, touch2)
return Math.sqrt(Math.pow(coord.x2 - coord.x1, 2) + Math.pow(coord.y2 - coord.y1, 2))
}
/**
* Pinch center around wich the canvas will be scaled/zoomed
*/
function setPinchCenter(touch1, touch2) {
const coord = getPinchCoordinates(touch1, touch2)
const currentX = (coord.x1 + coord.x2) / 2
const currentY = (coord.y1 + coord.y2) / 2
pinchCenter = {
x: currentX,
y: currentY,
}
}
You can easily add zoom with mouse wheel as well adding this:
canvas.on('mouse:wheel', zoomCanvasMouseWheel)
/**
* Zoom canvas when user used mouse wheel
*/
function zoomCanvasMouseWheel(event) {
const delta = event.e.deltaY
let zoom = canvas.getZoom()
zoom *= 0.999 ** delta
const point = {x: event.e.offsetX, y: event.e.offsetY}
zoomCanvas(zoom, point)
}
Again, this is a very expensive way to handle zoom and drag since it forces Fabric.js to re-render the content on every frame. Even when limiting the event calls using throttle you will not get smooth performance especially on mobile devices. Consider using an alternative method with CSS transform as I have described in this answer.