You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击导航链接时如何终止Canvas正在运行的动画并启动新动画?

嘿,我帮你搞定这个动画终止的问题!你当前的核心问题在于没正确管理动画帧的生命周期——既没保存动画ID来终止旧动画,isRunning的逻辑也有漏洞,导致多个动画能同时跑起来。下面给你拆解问题并给出完整的修复方案:

问题根源分析

  1. 无法终止旧动画:你没保存requestAnimationFrame返回的ID,所以调用cancelAnimationFrame时找不到目标帧。
  2. isRunning逻辑失效:你的animate函数每执行一次就把isRunning设为false,导致动画还在运行时,新的点击就能绕过判断启动新动画。
  3. 变量作用域混淆: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;
    }
  }
}

关键修复点说明

  1. 动画帧管理:用requestId保存每帧的ID,每次启动新动画前先终止旧动画,确保同一时间只有一个动画在运行。
  2. 画布清空:每次点击导航都先清空画布,彻底清除旧动画的痕迹。
  3. 作用域优化:把t、points和animate函数放在draw内部,确保每次点击的动画都使用独立的路径数据,不会互相干扰。
  4. 状态重置:动画结束后主动重置requestId,避免影响下一次动画启动。

这样修改后,不管你快速切换哪个导航链接,旧动画都会立即停止,新动画会从头开始绘制,再也不会出现多个动画叠加的问题啦!

内容的提问来源于stack exchange,提问作者Neniariel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:02:30