首页 > 解决方案 > 如何使用 HTML5 Canvas 平滑连续地加入 2 条贝塞尔曲线

问题描述

我正在尝试将两条单独的贝塞尔曲线连接成一条连续曲线。目前,我所拥有的看起来像这样:

两条独立的贝塞尔曲线

问题是它们没有连接,因此它们相遇的点看起来很尖/尖锐,而不是弯曲和光滑。我查看了在 P5.js 中加入贝塞尔曲线的文档,但不确定如何将其转换为 HTML5 Canvas。如何连接这两条贝塞尔曲线,使它们看起来像一条平滑连续的曲线?

这是我的代码:

const canvas = document.getElementById('canvas');
const c = canvas.getContext("2d");
width = 800;
height = 500;
canvas.width = width;
canvas.height = height;
let face;
let centerX = width / 2;
let centerY = height / 3;

setup();

function setup() {
    c.clearRect(0, 0, canvas.width, canvas.height);
    face = new Face();
    draw();
};

function draw() {
    setBackground(`rgba(250, 250, 250, 1)`);
    c.beginPath();
    c.moveTo(centerX - face.hsx, centerY + face.hsy);
    c.bezierCurveTo(centerX - face.hcp1x / 10, centerY - face.hsy2,
        centerX + face.hcp1x / 10, centerY - face.hsy2,
        centerX + face.hsx, centerY + face.hsy);
    c.moveTo(centerX - face.hsx, centerY + face.hsy);
    c.bezierCurveTo(centerX - face.hcp1x, centerY + face.hcp1y,
        centerX + face.hcp1x, centerY + face.hcp1y,
        centerX + face.hsx, centerY + face.hsy);
    c.stroke();
    c.fillStyle = (`rgba(25, 250, 211, 0)`);
    c.fill();
}

function setBackground(color) {
    c.fillStyle = color;
    c.fillRect(0, 0, width, height);
}

function Face() {
    this.hsx = 150; 
    this.hsy = 0;
    this.hsy2 = 120;
    this.hcp1x = 120;
    this.hcp1y = 250; 
}

标签: javascriptcanvashtml5-canvas

解决方案


公切线

要平滑地连接两个贝塞尔曲线,您需要使公共点的线平行,从而将两个贝塞尔曲线末端和起点的切线定义为相同。

下图说明了这一点

在此处输入图像描述

由两个控制点 (C 2 , C 1 ) 和公共点 (P) 定义的线是曲线在 P 处的切线。线段的长度没有约束。

如何?

有很多方法可以做到这一点,你如何做到这一点取决于曲线的要求、曲线的类型等等。

例子

我不打算给出一个完整的例子,因为它需要对向量数学的理解和一个​​涵盖所有解决方案的假设你不熟悉向量数学会很大。

因此最基本的伪代码示例使用前一个控制点和端点来计算下一个控制点。?表示不受保持线平行所需约束的未知数

 // From illustration in answer
 corner = ?        // Distance to next control point as fraction of distance
                   // from previous control point
 C2 = {x:?, y:?}   // Last control point of previous bezier
 P  = {x:?, y:?}   // Start of next bezier
 C1 = {            // Next control point along line from previous and scaled
     x: P.x + (P.x - C2.x) * corner,
     y: P.y + (P.y - C2.y) * corner,
 }

 // two beziers with common point P
 ctx.bezierCurveTo(?,?, C2.x, C2.y, P.x, P.y)  
 ctx.bezierCurveTo(C1.x, C1.y, ?, ?, ?, ?)

推荐阅读