如何在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
相关产品推荐
相关产品推荐

