Fabric.js修改视口或初始缩放后触摸坐标异常求助
嘿,我之前也碰到过几乎一模一样的Fabric.js移动端缩放问题,大概率是自定义视口/初始缩放后,Fabric的坐标转换逻辑没跟上导致的,和Canvas本身关系不大,咱们一步步排查解决:
问题根源
当你修改了视口宽度或者手动设置初始缩放比例后,Fabric.js内部用于将触摸点转换为Canvas内部坐标的映射关系被打乱了——默认情况下Fabric会基于Canvas的原始尺寸和默认缩放来计算坐标,一旦你改动了视口或初始缩放,这个映射就会出错,导致触摸时图片“找不准”位置,出现跳动。
具体解决步骤
1. 先锁死移动端视口,避免系统干扰
首先确保HTML头部的视口设置正确,防止浏览器默认的缩放、滚动行为影响Fabric的坐标计算:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
这里user-scalable=no是为了彻底隔离系统手势和Fabric的捏合缩放,先这么设置排查问题,后续如果需要保留系统缩放再做额外适配。
2. 同步Canvas尺寸与视口,正确设置初始缩放
不要只改视口就不管Canvas的实际尺寸,必须让Canvas的渲染尺寸(width/height属性)和显示尺寸(CSS尺寸)匹配,同时设置初始缩放时要通过viewportTransform来同步Fabric的内部状态:
// 获取Canvas DOM元素 const canvasElement = document.getElementById('editor'); // 让Canvas尺寸匹配当前视口 canvasElement.width = window.innerWidth; canvasElement.height = window.innerHeight; // 初始化Canvas,开启触摸支持并禁用干扰项 const canvas = new fabric.Canvas('editor', { enableTouchScroll: false, // 禁止触摸滚动,避免和缩放冲突 fireRightClick: true // 防止长按触发右键菜单 }); // 如果需要设置初始缩放比例(比如缩放到80%) const initialScale = 0.8; // 通过viewportTransform设置初始缩放,这是Fabric内部认可的缩放方式 canvas.setViewportTransform([initialScale, 0, 0, initialScale, 0, 0]); // 添加图片时让它居中,避免初始位置偏移 const fbImg = new fabric.Image(img, { left: canvas.width / 2, top: canvas.height / 2, originX: 'center', originY: 'center' }); canvas.add(fbImg);
3. 校准触摸坐标转换逻辑
有时候Fabric默认的触摸坐标转换在自定义缩放后会有偏差,你可以手动在触摸事件里校准坐标:
canvas.on('touch:gesture', function(e) { // 获取原始触摸点 const touch = e.e.touches[0]; // 使用Fabric自带的getPointer方法转换坐标,第二个参数true表示适配当前viewportTransform const pointer = canvas.getPointer(touch, true); // 强制设置手势的中心点为转换后的坐标,确保缩放围绕触摸点进行 e.center = pointer; });
如果还是有偏移,可以手动计算坐标(基于Canvas的DOM位置和当前缩放):
const rect = canvas.getElement().getBoundingClientRect(); const pointerX = (touch.clientX - rect.left) / canvas.getZoom(); const pointerY = (touch.clientY - rect.top) / canvas.getZoom();
4. 禁用Canvas的默认触摸行为
浏览器的默认触摸动作(比如滚动、双击缩放)可能会打断Fabric的手势逻辑,加上这段代码阻止默认行为:
canvas.getElement().addEventListener('touchstart', function(e) { e.preventDefault(); }, { passive: false });
这里passive: false是必须的,否则无法调用preventDefault。
总结
按照这个流程来:先锁死视口→同步Canvas尺寸与视口→用viewportTransform设置初始缩放→校准触摸坐标,基本就能解决图片跳动、触摸坐标异常的问题。如果还是不行,可以暂时移除所有自定义配置,回到初始正常状态,再一步步加配置排查是哪一步导致的问题。
内容的提问来源于stack exchange,提问作者greg

