如何让HTML Canvas绘图板支持触摸屏设备?
网页Canvas绘图工具适配触屏设备的实现方案
你的绘图工具在触屏设备失效,核心问题是直接替换touch事件时没处理触屏事件的特性——比如触屏坐标存储在touches集合中、默认页面行为会干扰绘图、多点触摸需要过滤。下面是具体的修复步骤和完整代码:
关键修复点
- 触屏坐标获取:触屏事件的坐标不在
e.clientX/Y,而是在e.touches[0].clientX/Y(取第一个触摸点) - 阻止默认行为:触屏时会触发页面滚动/缩放,需要在
touchstart和touchmove中阻止默认行为 - 统一事件逻辑:复用现有绘图逻辑,给鼠标和触屏事件绑定相同的处理函数,避免重复代码
- 实时偏移计算:用
getBoundingClientRect()替代固定的offsetLeft/offsetTop,确保窗口大小变化后坐标仍准确
修改后的完整JS代码
const canvas = document.getElementById('drawing-board'); const toolbar = document.getElementById('toolbar'); const ctx = canvas.getContext('2d'); // 实时获取canvas的位置偏移 function getCanvasOffset() { const rect = canvas.getBoundingClientRect(); return { x: rect.left, y: rect.top }; } // 初始化canvas尺寸 function resizeCanvas() { const offset = getCanvasOffset(); canvas.width = window.innerWidth - offset.x; canvas.height = window.innerHeight - offset.y; } resizeCanvas(); // 窗口大小变化时重新调整canvas尺寸 window.addEventListener('resize', resizeCanvas); let isPainting = false; let lineWidth = 5; let startX; let startY; toolbar.addEventListener('click', e => { if (e.target.id === 'clear') { ctx.clearRect(0, 0, canvas.width, canvas.height); } }); toolbar.addEventListener('change', e => { if(e.target.id === 'stroke') { ctx.strokeStyle = e.target.value; } if(e.target.id === 'lineWidth') { lineWidth = e.target.value; } }); // 统一的绘图函数,兼容鼠标和触屏事件 const draw = (e) => { if(!isPainting) return; // 根据事件类型获取坐标 const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX; const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY; const offset = getCanvasOffset(); ctx.lineWidth = lineWidth; ctx.lineCap = 'round'; ctx.lineTo(clientX - offset.x, clientY - offset.y); ctx.stroke(); }; // 开始绘图的处理函数 const startPainting = (e) => { isPainting = true; const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX; const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY; const offset = getCanvasOffset(); startX = clientX - offset.x; startY = clientY - offset.y; ctx.beginPath(); ctx.moveTo(startX, startY); // 阻止触屏默认行为(滚动、缩放) if (e.type.includes('touch')) { e.preventDefault(); } }; // 结束绘图的处理函数 const stopPainting = () => { isPainting = false; ctx.stroke(); ctx.beginPath(); }; // 绑定鼠标事件 canvas.addEventListener('mousedown', startPainting); canvas.addEventListener('mouseup', stopPainting); canvas.addEventListener('mouseleave', stopPainting); // 鼠标离开画布也停止绘图 canvas.addEventListener('mousemove', draw); // 绑定触屏事件 canvas.addEventListener('touchstart', startPainting); canvas.addEventListener('touchend', stopPainting); canvas.addEventListener('touchmove', (e) => { draw(e); e.preventDefault(); // 阻止触屏滑动时的页面滚动 });
额外说明
- 新增了
resize事件监听,窗口大小变化时自动调整canvas尺寸,避免绘图区域错位 - 增加了
mouseleave事件处理,鼠标离开画布时自动停止绘图,提升体验 - 触屏事件中两次调用
preventDefault,分别阻止初始触摸和滑动时的默认行为,确保绘图流畅
内容的提问来源于stack exchange,提问作者m4rk
相关产品推荐
相关产品推荐

