FabricJS是一款强大的Canvas交互库,很多前端项目会借助它实现自由绘图功能,但在iOS和Android的触摸屏设备上,经常会出现触摸屏幕无法绘制线条、绘制响应不灵敏等失效问题,这些问题大多和移动端事件特性、FabricJS配置以及浏览器默认行为有关。

常见失效原因分析
要解决FabricJS自由绘图在触摸屏失效的问题,首先需要明确常见的触发原因:
- 未正确开启FabricJS的自由绘图模式,或者绘图配置参数不符合移动端场景
- 触摸事件被浏览器默认行为拦截,比如触摸时页面滚动、缩放覆盖了绘图事件
- 移动端浏览器的触摸事件和鼠标事件存在差异,FabricJS默认的事件监听未覆盖触摸场景
- 画布容器的CSS样式问题,比如阻止了触摸事件的冒泡或者默认行为
完整解决方案
1. 基础配置开启自由绘图
首先要确保FabricJS的画布已经正确开启自由绘图模式,同时设置适配移动端的参数。下面是基础的初始化代码:
// 初始化FabricJS画布
const canvas = new fabric.Canvas('drawing-canvas', {
isDrawingMode: true, // 开启自由绘图模式
// 移动端触摸时需要调整笔刷的粗细,避免触摸面积大导致线条过粗
freeDrawingBrush: {
width: 3,
color: '#000000'
}
});
// 确认绘图模式已开启,也可以在后续动态切换
canvas.isDrawingMode = true;
2. 处理浏览器默认触摸行为
移动端浏览器默认会在触摸时触发页面滚动、缩放等行为,这些行为会拦截FabricJS的触摸绘图事件,需要在画布容器上阻止默认行为。注意不要全局阻止,只针对画布区域处理:
// 获取画布容器元素
const canvasEl = document.getElementById('drawing-canvas');
// 阻止触摸时的默认行为,避免页面滚动覆盖绘图事件
canvasEl.addEventListener('touchstart', function(e) {
e.preventDefault();
}, { passive: false });
canvasEl.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: false });
这里要注意添加passive: false参数,否则部分浏览器会忽略preventDefault的调用,导致拦截失效。
3. 适配iOS和Android的事件差异
iOS和Android的触摸事件存在细微差异,部分场景下FabricJS默认的鼠标事件监听无法覆盖触摸事件,需要手动补充事件绑定逻辑:
// 监听触摸开始事件,手动触发FabricJS的鼠标按下逻辑
canvasEl.addEventListener('touchstart', function(e) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvasEl.dispatchEvent(mouseEvent);
});
// 监听触摸移动事件,手动触发FabricJS的鼠标移动逻辑
canvasEl.addEventListener('touchmove', function(e) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvasEl.dispatchEvent(mouseEvent);
});
// 监听触摸结束事件,手动触发FabricJS的鼠标抬起逻辑
canvasEl.addEventListener('touchend', function(e) {
const mouseEvent = new MouseEvent('mouseup');
canvasEl.dispatchEvent(mouseEvent);
});
4. 调整画布容器的CSS样式
画布容器的CSS样式也可能影响触摸事件的响应,需要确保容器没有设置阻止事件的相关属性:
/* 画布容器样式 */
#drawing-canvas {
width: 100%;
height: 500px;
/* 确保触摸事件可以正常触发 */
touch-action: none;
/* 避免容器有溢出隐藏等影响事件传递的样式 */
overflow: visible;
}
这里的touch-action: none是CSS的触摸行为控制属性,设置为none可以禁止浏览器对触摸事件的默认处理,进一步避免和绘图事件冲突。
完整示例代码
下面是整合了所有解决方案的完整可运行代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>FabricJS触摸屏自由绘图</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.0/fabric.min.js"></script>
<style>
#drawing-canvas {
width: 100%;
height: 500px;
border: 1px solid #ccc;
touch-action: none;
}
</style>
</head>
<body>
<canvas id="drawing-canvas"></canvas>
<script>
// 初始化画布
const canvas = new fabric.Canvas('drawing-canvas', {
isDrawingMode: true,
freeDrawingBrush: {
width: 3,
color: '#000000'
}
});
const canvasEl = document.getElementById('drawing-canvas');
// 阻止默认触摸行为
canvasEl.addEventListener('touchstart', function(e) {
e.preventDefault();
}, { passive: false });
canvasEl.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: false });
// 适配触摸事件和鼠标事件
canvasEl.addEventListener('touchstart', function(e) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvasEl.dispatchEvent(mouseEvent);
});
canvasEl.addEventListener('touchmove', function(e) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
canvasEl.dispatchEvent(mouseEvent);
});
canvasEl.addEventListener('touchend', function(e) {
const mouseEvent = new MouseEvent('mouseup');
canvasEl.dispatchEvent(mouseEvent);
});
</script>
</body>
</html>
注意事项
如果项目中使用了iframe嵌套画布,还需要确保iframe的容器也设置了touch-action: none,并且没有拦截触摸事件。另外在iOS的Safari浏览器中,如果遇到绘图延迟的问题,可以适当降低笔刷的宽度,或者调整画布的分辨率适配屏幕像素比。按照以上步骤调整后,FabricJS的自由绘图功能就可以在iOS和Android的触摸屏设备上正常使用了,开发者可以根据项目的实际需求调整笔刷颜色、粗细等参数。