touchstart事件导致Canvas游戏卡顿的原因及纯JS解决方案
解决触屏事件导致Canvas动画卡顿的问题
导致卡顿的核心原因有两个:
- 使用
setInterval驱动动画,它的执行时机不与浏览器渲染帧同步,容易出现丢帧和卡顿; - 触屏事件会触发浏览器默认行为(如页面滚动、触摸缩放),这些操作会抢占主线程资源,打断动画执行。
以下是修复后的纯JavaScript实现,同时优化了动画流畅性:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <!-- 添加viewport元标签,确保触屏设备页面显示正常 --> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> </head> <body> <canvas id="cnvs" width="400" height="400"></canvas> <script> var canvas, canvasContext; var x = 0, y = 30; var speed = 300; // 每秒移动300像素 var lastTimestamp = 0; window.onload = function() { canvas = document.getElementById('cnvs'); canvasContext = canvas.getContext('2d'); // 启动浏览器优化的动画循环 requestAnimationFrame(updateAll); canvas.addEventListener('touchstart', touchStart); canvas.addEventListener('touchend', touchEnd); } function touchStart(e) { e.preventDefault(); // 阻止浏览器默认触屏行为,避免抢占资源 console.log(e); } function touchEnd(e) { e.preventDefault(); // 阻止浏览器默认触屏行为 console.log(e); } function colorRect(x, y, width, height, color) { canvasContext.fillStyle = color; canvasContext.fillRect(x, y, width, height); } function updateAll(timestamp) { // 计算时间差,实现基于时间的动画增量,保证不同设备速度一致 const deltaTime = (timestamp - lastTimestamp) / 1000; lastTimestamp = timestamp; moveAll(deltaTime); drawAll(); // 持续调度下一帧动画 requestAnimationFrame(updateAll); } function moveAll(deltaTime) { x += speed * deltaTime; if(x > canvas.width - 20 || x < 0) { speed = -speed; } } function drawAll() { colorRect(0, 0, canvas.width, canvas.height, 'black'); colorRect(x, y, 20, 20, 'red'); } </script> </body> </html>
关键优化点说明:
- 替换
setInterval为requestAnimationFrame:该API由浏览器调度,与屏幕刷新频率同步,能最大程度避免丢帧; - 阻止触屏默认行为:通过
e.preventDefault()禁用触摸滚动、缩放等操作,防止这些行为抢占主线程打断动画; - 基于时间的动画增量:不再用固定步长移动,而是根据帧间隔计算移动距离,确保动画速度在不同帧率设备上一致;
- 添加viewport元标签:禁用用户缩放,避免额外浏览器行为干扰动画。
内容的提问来源于stack exchange,提问作者Azariah Easey
相关产品推荐
相关产品推荐

