如何绘制经过指定控制点的Canvas二次贝塞尔曲线
让Canvas二次贝塞尔曲线经过指定点的实现方法
你当前的代码是把目标点当作二次贝塞尔曲线的控制点来使用,但二次贝塞尔的控制点本身并不在曲线上,只是用于控制曲线的弯曲方向。要让曲线经过指定点,需要通过贝塞尔曲线的参数方程反推真正的控制点。
核心原理
二次贝塞尔曲线的参数方程为(t取值范围[0,1]):
B(t) = (1-t)²P₀ + 2t(1-t)P_c + t²P₁
其中:
P₀是曲线起点,P₁是曲线终点P_c是贝塞尔控制点(不在曲线上)- 若要曲线经过目标点
Q,只需代入Q=B(t),即可解出对应的P_c。
方法一:让目标点作为曲线中点(t=0.5)
当目标点是曲线的中点时,参数方程可以简化为:
P_c = 2Q - (P₀ + P₁)/2
对应代码实现:
const startPoints = [0, 0]; const targetPoint = [100, 40]; // 你希望曲线经过的点 const endPoints = [200, 200]; // 计算贝塞尔控制点 const getBezierControl = (p0, q, p1) => { return [ 2 * q[0] - (p0[0] + p1[0]) / 2, 2 * q[1] - (p0[1] + p1[1]) / 2 ]; }; const bezierControl = getBezierControl(startPoints, targetPoint, endPoints); // 绘制曲线 ctx.moveTo(...startPoints); ctx.quadraticCurveTo(...bezierControl, ...endPoints); ctx.stroke();
代入你的坐标计算后,贝塞尔控制点为[100, -20],绘制出的曲线会准确经过(100, 40)。
方法二:指定目标点在曲线上的任意位置(自定义t值)
如果需要目标点在曲线的任意位置(比如t=0.3,即从起点到终点30%的位置),使用通用公式计算:
P_c = [Q - (1-t)²P₀ - t²P₁] / [2t(1-t)]
对应代码:
const startPoints = [0, 0]; const targetPoint = [100, 40]; const endPoints = [200, 200]; const t = 0.3; // 目标点对应的参数值 const getBezierControl = (p0, q, p1, t) => { const denominator = 2 * t * (1 - t); return [ (q[0] - Math.pow(1 - t, 2) * p0[0] - Math.pow(t, 2) * p1[0]) / denominator, (q[1] - Math.pow(1 - t, 2) * p0[1] - Math.pow(t, 2) * p1[1]) / denominator ]; }; const bezierControl = getBezierControl(startPoints, targetPoint, endPoints, t); // 绘制曲线 ctx.moveTo(...startPoints); ctx.quadraticCurveTo(...bezierControl, ...endPoints); ctx.stroke();
总结
二次贝塞尔曲线的控制点和“曲线上的点”是完全不同的概念,通过参数方程反推是实现需求的核心。你可以根据需要选择目标点在曲线上的位置,计算出对应的控制点后再绘制曲线。
内容的提问来源于stack exchange,提问作者mediumgoal
相关产品推荐
相关产品推荐

