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

如何在HTML中绘制多段颜色的三次贝塞尔曲线?

分色分段绘制三次贝塞尔曲线(Canvas/SVG实现)

不管是Canvas还是SVG,都没办法直接按参数t值绑定描边颜色,但可以通过拆分t区间分段绘制,或者用渐变模拟(有局限性),下面是具体实现方案:

Canvas 实现方案

1. 拆分t区间分段绘制(精准方案)

三次贝塞尔曲线可以通过德卡斯特里奥算法拆分出任意t=x处的子曲线,得到两段独立的贝塞尔曲线后,分别设置颜色绘制即可。

假设原三次贝塞尔曲线的控制点为 P0(x0,y0), P1(x1,y1), P2(x2,y2), P3(x3,y3),要在t=x处拆分,步骤如下:

  • 计算中间插值点:
    const splitT = 0.3; // 示例拆分点t=0.3
    // 第一层插值
    const A = {x: (1-splitT)*P0.x + splitT*P1.x, y: (1-splitT)*P0.y + splitT*P1.y};
    const B = {x: (1-splitT)*P1.x + splitT*P2.x, y: (1-splitT)*P1.y + splitT*P2.y};
    const C = {x: (1-splitT)*P2.x + splitT*P3.x, y: (1-splitT)*P2.y + splitT*P3.y};
    // 第二层插值
    const D = {x: (1-splitT)*A.x + splitT*B.x, y: (1-splitT)*A.y + splitT*B.y};
    const E = {x: (1-splitT)*B.x + splitT*C.x, y: (1-splitT)*B.y + splitT*C.y};
    // 第三层插值(拆分点)
    const F = {x: (1-splitT)*D.x + splitT*E.x, y: (1-splitT)*D.y + splitT*E.y};
    
  • 绘制两段曲线:
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    
    // 第一段:t=0到t=splitT,颜色1
    ctx.strokeStyle = '#ff0000';
    ctx.beginPath();
    ctx.moveTo(P0.x, P0.y);
    ctx.bezierCurveTo(A.x, A.y, D.x, D.y, F.x, F.y);
    ctx.stroke();
    
    // 第二段:t=splitT到t=1,颜色2
    ctx.strokeStyle = '#00ff00';
    ctx.beginPath();
    ctx.moveTo(F.x, F.y);
    ctx.bezierCurveTo(E.x, E.y, C.x, C.y, P3.x, P3.y);
    ctx.stroke();
    

2. 线性渐变模拟(近似方案)

如果曲线弯曲程度不大,可以用线性渐变模拟分色效果:计算t=x对应的曲线上点,创建从起点到该点为颜色1、该点到终点为颜色2的线性渐变。但注意渐变是沿直线的,曲线弯曲时颜色过渡会偏离t区间。

// 先计算t=x处的点F(用上面的方法)
const gradient = ctx.createLinearGradient(P0.x, P0.y, P3.x, P3.y);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(splitT, '#ff0000');
gradient.addColorStop(splitT, '#00ff00');
gradient.addColorStop(1, '#00ff00');

ctx.strokeStyle = gradient;
ctx.beginPath();
ctx.moveTo(P0.x, P0.y);
ctx.bezierCurveTo(P1.x, P1.y, P2.x, P2.y, P3.x, P3.y);
ctx.stroke();

SVG 实现方案

1. 拆分t区间绘制两段路径(精准方案)

和Canvas逻辑一致,拆分控制点后生成两个<path>元素,分别设置描边颜色:

<svg width="400" height="300">
  <!-- 原曲线控制点:P0(50,250), P1(100,50), P2(300,50), P3(350,250) -->
  <!-- 拆分t=0.3后的子曲线 -->
  <path d="M50 250 C 65 190 83.5 137 104.55 92.55" stroke="#ff0000" fill="none" stroke-width="3"/>
  <path d="M104.55 92.55 C 125.6 48.1 235 50 350 250" stroke="#00ff00" fill="none" stroke-width="3"/>
</svg>

2. 利用stroke-dasharray实现分段着色(简便方案)

先绘制整个曲线的颜色1,再绘制颜色2的后半段,通过stroke-dasharray控制只显示t=x到t=1的部分:

<svg width="400" height="300" id="svg">
  <path id="bezier" d="M50 250 C 100 50 300 50 350 250" stroke="#ff0000" fill="none" stroke-width="3"/>
</svg>

<script>
  const path = document.getElementById('bezier');
  const totalLength = path.getTotalLength();
  const splitT = 0.3;
  const dashLength = totalLength * (1 - splitT);
  
  // 创建颜色2的路径,只显示后半段
  const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
  path2.setAttribute('d', path.getAttribute('d'));
  path2.setAttribute('stroke', '#00ff00');
  path2.setAttribute('fill', 'none');
  path2.setAttribute('stroke-width', '3');
  path2.setAttribute('stroke-dasharray', `${dashLength} ${totalLength}`);
  path2.setAttribute('stroke-dashoffset', `- ${totalLength * splitT}`);
  
  document.getElementById('svg').appendChild(path2);
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:45:30