点击导航链接时如何终止Canvas正在运行的动画并启动新动画?
嘿,我帮你搞定这个动画终止的问题!你当前的核心问题在于没正确管理动画帧的生命周期——既没保存动画ID来终止旧动画,isRunning的逻辑也有漏洞,导致多个动画能同时跑起来。下面给你拆解问题并给出完整的修复方案:
问题根源分析
- 无法终止旧动画:你没保存
requestAnimationFrame返回的ID,所以调用cancelAnimationFrame时找不到目标帧。 isRunning逻辑失效:你的animate函数每执行一次就把isRunning设为false,导致动画还在运行时,新的点击就能绕过判断启动新动画。- 变量作用域混淆:
points变量依赖闭包,但多次调用draw时容易出现旧数据干扰新动画的情况。
修复后的完整代码
HTML部分
<div class="container"> <nav> <ul> <li> <a href="#about" id="about" onclick="draw(this.id);">About</a> </li> <li> <a href="#contact" id="contact" onclick="draw(this.id);">Work</a> </li> </ul> </nav> </div> <canvas id="canvas" width="500" height="500"></canvas>
JavaScript部分
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var requestId = null; // 保存当前动画的帧ID var border = 2; // 补上你代码中用到但未定义的border变量 function draw(contentId) { // 第一步:终止正在运行的旧动画 if (requestId) { window.cancelAnimationFrame(requestId); requestId = null; } // 第二步:清空画布,避免旧动画残留 ctx.clearRect(0, 0, canvas.width, canvas.height); var vertices = []; var points; // 第三步:根据点击的导航设置对应路径和样式 if (contentId === 'about') { vertices.push({x: 50, y: 0}, {x: 50, y: 50}); vertices.push({x: 40, y: 60}, {x: 0, y: 60}); ctx.lineCap = "round"; ctx.lineWidth = border; ctx.strokeStyle = "green"; points = calcWaypoints(vertices); } if (contentId === 'contact') { vertices.push({x: 150, y: 0}, {x: 150, y: 50}); vertices.push({x: 160, y: 60}, {x: 300, y: 60}); vertices.push({x: 300, y: 70}, {x: 310, y: 70}); ctx.lineCap = "round"; ctx.lineWidth = border; ctx.strokeStyle = "green"; points = calcWaypoints(vertices); } // 第四步:启动新动画 var t = 1; animate(); // 计算路径点的工具函数 function calcWaypoints(vertices) { var waypoints = []; for (var i = 1; i < vertices.length; i++) { var pt0 = vertices[i - 1]; var pt1 = vertices[i]; var dx = pt1.x - pt0.x; var dy = pt1.y - pt0.y; for (var j = 0; j < 100; j++) { var x = pt0.x + dx * j / 100; var y = pt0.y + dy * j / 100; waypoints.push({ x: x, y: y }); } } return waypoints; } // 动画执行函数 function animate() { if (t < points.length) { ctx.beginPath(); ctx.moveTo(points[t - 1].x, points[t - 1].y); ctx.lineTo(points[t].x, points[t].y); ctx.stroke(); t++; // 保存当前动画帧ID,方便后续终止 requestId = window.requestAnimationFrame(animate); } else { // 动画结束后重置ID requestId = null; } } }
关键修复点说明
- 动画帧管理:用
requestId保存每帧的ID,每次启动新动画前先终止旧动画,确保同一时间只有一个动画在运行。 - 画布清空:每次点击导航都先清空画布,彻底清除旧动画的痕迹。
- 作用域优化:把
t、points和animate函数放在draw内部,确保每次点击的动画都使用独立的路径数据,不会互相干扰。 - 状态重置:动画结束后主动重置
requestId,避免影响下一次动画启动。
这样修改后,不管你快速切换哪个导航链接,旧动画都会立即停止,新动画会从头开始绘制,再也不会出现多个动画叠加的问题啦!
内容的提问来源于stack exchange,提问作者Neniariel
相关产品推荐
相关产品推荐

