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

如何用React.js和TailwindCSS实现响应式双色曲线进度条?

曲线进度条+沿轨迹移动火箭实现方案

核心思路

解决这个问题需要拆分两个核心需求:曲线进度条的逐步填充和火箭沿曲线轨迹的定向移动,用SVG原生特性+CSS/JS配合就能实现,不需要复杂的堆叠元素。


1. 曲线进度条填充实现

利用SVG的stroke-dasharray和stroke-dashoffset属性实现路径的渐进填充:

  • 导出SVG时保留两条完全重合的曲线:一条作为浅色背景,一条作为深色进度条
  • 计算深色曲线的总长度,通过调整stroke-dashoffset控制填充范围(初始从中间位置开始,即dashoffset = 总长度/2)

代码示例(SVG结构)

<svg viewBox="0 0 800 600" class="w-full h-64">
  <!-- 浅色背景曲线 -->
  <path 
    d="M200,300 C400,100 600,500 800,300" 
    stroke="#e5e7eb" 
    stroke-width="8" 
    fill="none"
  />
  <!-- 深色进度曲线 -->
  <path 
    id="progress-curve" 
    d="M200,300 C400,100 600,500 800,300" 
    stroke="#1f2937" 
    stroke-width="8" 
    fill="none"
  />
</svg>

JS控制填充逻辑

const progressCurve = document.getElementById('progress-curve');
const totalLength = progressCurve.getTotalLength();
const startOffset = totalLength / 2; // 初始从曲线中间开始

// 初始化dash参数
progressCurve.style.strokeDasharray = totalLength;
progressCurve.style.strokeDashoffset = startOffset;

// 按步骤更新进度(示例:4步完成从中间到端点的填充)
function updateProgress(step) {
  // 每步填充总长度的1/8(因为中间到端点是总长度的50%)
  const newOffset = startOffset - (step * (totalLength / 8));
  progressCurve.style.strokeDashoffset = Math.max(newOffset, 0);
}

// 调用示例:执行第一步
updateProgress(1);

2. 火箭沿曲线轨迹移动

有两种可靠方案,选一种适合你的场景:

方案A:CSS Offset Path(推荐,配合Tailwind)

利用CSS的offset-path属性绑定曲线轨迹,offset-rotate: auto让火箭始终贴合曲线方向:

  1. 自定义Tailwind工具类(在tailwind.config.js或自定义CSS中):
@layer utilities {
  .follow-curve {
    offset-path: path('M200,300 C400,100 600,500 800,300');
    offset-rotate: auto;
  }
}
  1. 把火箭作为独立元素加入SVG,用CSS控制位置:
<svg viewBox="0 0 800 600" class="w-full h-64">
  <!-- 背景曲线、进度曲线 -->
  <!-- 火箭元素 -->
  <g id="rocket" class="follow-curve transition-all duration-700">
    <path d="M0,-10 L5,10 L-5,10 Z" fill="#f59e0b" />
    <circle cx="0" cy="0" r="3" fill="#1f2937" />
  </g>
</svg>
  1. JS同步火箭位置与进度:
const rocket = document.getElementById('rocket');

function updateProgress(step) {
  // ... 之前的进度更新代码
  // 火箭位置从50%(中间)到100%(端点),每步增加12.5%
  const distancePercent = 50 + (step * 12.5);
  rocket.style.offsetDistance = `${distancePercent}%`;
}

方案B:SVG原生animateMotion

如果更倾向于纯SVG动画,用<animateMotion>标签直接绑定轨迹:

<g id="rocket">
  <path d="M0,-10 L5,10 L-5,10 Z" fill="#f59e0b" />
  <circle cx="0" cy="0" r="3" fill="#1f2937" />
  <animateMotion
    dur="3s"
    begin="click" <!-- 可替换为JS触发 -->
    from="0.5" <!-- 从路径50%位置开始 -->
    to="1" <!-- 到路径100%位置结束 -->
    path="M200,300 C400,100 600,500 800,300"
    rotate="auto"
    fill="freeze" <!-- 动画结束后保持最终状态 -->
  />
</g>

关键注意事项

  • 导出SVG时确保曲线是单一连续路径,避免被拆分,方便提取d属性值
  • 用getTotalLength()计算路径长度时,要确保SVG的viewBox设置正确,避免坐标偏差
  • 可以结合Tailwind的transition类实现平滑的进度过渡和火箭移动动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:35:54