You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fabric.JS移动端Zoom缩放与Move平移功能失效问题求助

Fabric.js 触摸事件适配问题修复

问题描述

  • 鼠标滚轮可实现画布缩放,但移动端触屏无法完成缩放操作;
  • 电脑端通过Alt+左键可平移画布,但移动端触屏无法平移画布。

修复方案

移动端双指缩放

利用Fabric.js的touch:gesture事件,通过事件对象的scale属性获取双指缩放比例,结合当前画布缩放值计算新的缩放级别,并以双指中心点作为缩放原点:

canvas.on('touch:gesture', function(e) {
  if (e.scale && e.scale !== 1) {
    var currentZoom = canvas.getZoom();
    var newZoom = currentZoom * e.scale;
    // 限制缩放范围
    newZoom = Math.max(0.01, Math.min(20, newZoom));
    // 以双指中心点为缩放原点
    var centerPoint = {
      x: e.center.x,
      y: e.center.y
    };
    canvas.zoomToPoint(centerPoint, newZoom);
    e.e.preventDefault();
    e.e.stopPropagation();
  }
});

移动端单指平移

通过touch:drag事件实现单指平移,逻辑与PC端平移一致,计算触摸位移来更新视口变换参数:

canvas.on('touch:drag', function(e) {
  var pointer = canvas.getPointer(e.e);
  if (!canvas.isDragging) {
    canvas.isDragging = true;
    canvas.selection = false;
    canvas.lastPosX = pointer.x;
    canvas.lastPosY = pointer.y;
  } else {
    var vpt = canvas.viewportTransform;
    vpt[4] += pointer.x - canvas.lastPosX;
    vpt[5] += pointer.y - canvas.lastPosY;
    canvas.requestRenderAll();
    canvas.lastPosX = pointer.x;
    canvas.lastPosY = pointer.y;
  }
  e.e.preventDefault();
  e.e.stopPropagation();
});

canvas.on('touch:up', function() {
  canvas.setViewportTransform(canvas.viewportTransform);
  canvas.isDragging = false;
  canvas.selection = true;
});

修改后的完整代码

HTML

<div id="info"></div>
<div id="canvasWrapper">
  <canvas id="step1" width="400" height="400" style="border: solid gray thin;"></canvas>
</div>

JavaScript

var canvas = new fabric.Canvas('step1');
canvas.add(new fabric.Rect({ width: 50, height: 50, fill: 'blue', angle: 10 }))
canvas.add(new fabric.Circle({ radius: 50, fill: 'red', top: 44, left: 80 }))
canvas.add(new fabric.Ellipse({ rx: 50, ry: 10, fill: 'yellow', top: 80, left: 35 }))
canvas.add(new fabric.Rect({ width: 50, height: 50, fill: 'purple', angle: -19, top: 70, left: 70 }))
canvas.add(new fabric.Circle({ radius: 50, fill: 'green', top: 110, left: 30 }))
canvas.add(new fabric.Ellipse({ rx: 50, ry: 10, fill: 'orange', top: 12, left: 100, angle: 30 }))

// 电脑端滚轮缩放
canvas.on('mouse:wheel', function(opt) {
  var delta = opt.e.deltaY;
  var zoom = canvas.getZoom();
  zoom *= 0.999 ** delta;
  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();
});

// 电脑端Alt+左键平移
canvas.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;
  }
});

canvas.on('mouse:move', function(opt) {
  if (this.isDragging) {
    var e = opt.e;
    var vpt = this.viewportTransform;
    vpt[4] += e.clientX - this.lastPosX;
    vpt[5] += e.clientY - this.lastPosY;
    this.requestRenderAll();
    this.lastPosX = e.clientX;
    this.lastPosY = e.clientY;
  }
});

canvas.on('mouse:up', function(opt) {
  this.setViewportTransform(this.viewportTransform);
  this.isDragging = false;
  this.selection = true;
});

// 移动端双指缩放
canvas.on('touch:gesture', function(e) {
  if (e.scale && e.scale !== 1) {
    var currentZoom = canvas.getZoom();
    var newZoom = currentZoom * e.scale;
    newZoom = Math.max(0.01, Math.min(20, newZoom));
    var centerPoint = {
      x: e.center.x,
      y: e.center.y
    };
    canvas.zoomToPoint(centerPoint, newZoom);
    e.e.preventDefault();
    e.e.stopPropagation();
  }
});

// 移动端单指平移
canvas.on('touch:drag', function(e) {
  var pointer = canvas.getPointer(e.e);
  if (!canvas.isDragging) {
    canvas.isDragging = true;
    canvas.selection = false;
    canvas.lastPosX = pointer.x;
    canvas.lastPosY = pointer.y;
  } else {
    var vpt = canvas.viewportTransform;
    vpt[4] += pointer.x - canvas.lastPosX;
    vpt[5] += pointer.y - canvas.lastPosY;
    canvas.requestRenderAll();
    canvas.lastPosX = pointer.x;
    canvas.lastPosY = pointer.y;
  }
  e.e.preventDefault();
  e.e.stopPropagation();
});

canvas.on('touch:up', function() {
  canvas.setViewportTransform(canvas.viewportTransform);
  canvas.isDragging = false;
  canvas.selection = true;
});

// 保留原触摸事件日志(可选)
var info = document.getElementById('info');
canvas.on({
  'touch:orientation': function() {
    var text = document.createTextNode(' Orientation ');
    info.insertBefore(text, info.firstChild);
  },
  'touch:shake': function() {
    var text = document.createTextNode(' Shaking ');
    info.insertBefore(text, info.firstChild);
  },
  'touch:longpress': function() {
    var text = document.createTextNode(' Longpress ');
    info.insertBefore(text, info.firstChild);
  },
});

内容的提问来源于stack exchange,提问作者Adeeba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 18:57:07