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

如何使用纯JavaScript实现setAttribute修改SVG路径的平滑过渡效果

SVG路径d属性平滑过渡的纯JS实现方案

要实现d属性的平滑过渡,必须保证起始路径和目标路径的SVG路径命令类型、数量完全一致,仅坐标数值不同,否则无法进行线性插值,会出现异常变形。

方案1:使用Web Animations API(推荐)

这是浏览器原生支持的动画接口,无需引入第三方库,代码简洁,效果和SMIL完全一致,现代浏览器均支持。
你的SVG可以简化为不带SMIL标签的版本:

<svg viewBox="0 0 600 600">
  <path id="foo" d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="black"/>
</svg>

对应的JS逻辑:

const path = document.getElementById('foo');
// 定义起始和目标路径d值
const startD = 'M10 10 C 20 20, 40 20, 50 10';
const endD = 'M500 500 C 20 20, 40 20, 50 10';

path.addEventListener('click', () => {
  path.animate(
    // 关键帧
    [
      { d: startD },
      { d: endD }
    ],
    // 动画配置
    {
      duration: 5000, // 动画时长5秒,对应SMIL的dur="5s"
      iterations: Infinity, // 无限循环,对应repeatCount="indefinite",单次播放改为1即可
      fill: 'forwards' // 动画结束后保持最后一帧状态
    }
  );
});

方案2:手动实现帧插值(兼容老旧浏览器)

如果需要兼容不支持Web Animations API的老旧环境,可以自行解析路径数值,通过requestAnimationFrame逐帧插值更新d属性。
完整代码示例:

const path = document.getElementById('foo');
const startD = 'M10 10 C 20 20, 40 20, 50 10';
const endD = 'M500 500 C 20 20, 40 20, 50 10';
const duration = 5000; // 动画时长5秒

// 提取路径字符串中所有数值的工具函数
function getPathNumbers(d) {
  return d.match(/[-+]?\d*\.?\d+/g).map(Number);
}

// 把插值后的数值替换回路径模板,生成当前帧d值
function buildPath(template, numbers) {
  let numIndex = 0;
  return template.replace(/[-+]?\d*\.?\d+/g, () => numbers[numIndex++].toFixed(2));
}

// 预解析路径的命令结构和数值
const startNumbers = getPathNumbers(startD);
const endNumbers = getPathNumbers(endD);
// 生成保留命令结构、数值替换为占位符的模板
const pathTemplate = startD.replace(/[-+]?\d*\.?\d+/g, '{}');

path.addEventListener('click', () => {
  const startTime = performance.now();
  
  function animateFrame(currentTime) {
    const elapsed = currentTime - startTime;
    let progress = Math.min(elapsed / duration, 1);
    // 如需调整动画缓动效果,可在这里修改progress的计算逻辑,替换为对应缓动函数即可
    
    // 线性插值计算当前帧的所有路径数值
    const currentNumbers = startNumbers.map((startVal, index) => {
      return startVal + (endNumbers[index] - startVal) * progress;
    });
    
    // 更新路径属性
    path.setAttribute('d', buildPath(pathTemplate, currentNumbers));
    
    // 动画未结束则继续请求下一帧
    if (progress < 1) {
      requestAnimationFrame(animateFrame);
    }
    
    // 如需无限循环,把上面的if判断替换为以下两行即可
    // if (progress >= 1) startTime = currentTime;
    // requestAnimationFrame(animateFrame);
  }
  
  requestAnimationFrame(animateFrame);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 09:42:00