简体   繁体   English

如何使用 HTML5 Canvas 平滑连续地连接 2 条贝塞尔曲线

[英]How to join 2 Bezier curves smoothly & continuously with HTML5 Canvas

I'm trying to join two separate bezier curves into one continuous curve.我试图将两条单独的贝塞尔曲线连接成一条连续曲线。 Currently, what I have looks like this:目前,我所拥有的如下所示:

两条独立的贝塞尔曲线

The problem is that they aren't joined, so the points at which they meet look pointy/sharp instead of curvy and smooth.问题是它们没有连接,所以它们相遇的点看起来很尖/尖锐,而不是弯曲和光滑。 I've looked into documentation for joining bezier curves in P5.js, but am unsure of how to translate this into HTML5 Canvas.我查看了在 P5.js 中加入贝塞尔曲线的文档,但不确定如何将其转换为 HTML5 Canvas。 How do I join these two bezier curves so that they look like one smooth and continuous curve?如何连接这两条贝塞尔曲线,使它们看起来像一条平滑且连续的曲线?

This is my code:这是我的代码:

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; 
}

Common tangent公切线

To join two beziers smoothly you need to make the lines from the common point parallel thus defining the tangent at the end and start of the two beziers to be the same.要平滑地连接两个贝塞尔曲线,您需要使来自公共点的线平行,从而将两个贝塞尔曲线的末端和起点的切线定义为相同。

The following image illustrates this下图说明了这一点

在此处输入图片说明

The line that is defined by the two control points (C 2 , C 1 ) and the common point (P) is the tangent of the curve at P. The length of the line segments have no constraints.由两个控制点 (C 2 , C 1 ) 和公共点 (P) 定义的线是曲线在 P 处的切线。线段的长度没有限制。

How?如何?

There are dozens of ways to do this and how you do it is dependent on the requirements of the curve, the type of curve, and much more.有几十种方法可以做到这一点,而如何做到这一点取决于曲线的要求、曲线的类型等等。

Example例子

I am not going to give a full example as it requires an understanding of vector maths and a cover all solution on the assumption you are not familiar with vector maths would be huge.我不会给出一个完整的例子,因为它需要对向量数学的理解,并且假设你不熟悉向量数学会是一个巨大的假设,涵盖所有的解决方案。

Thus the most basic pseudo code example uses the previous control and end points to calculate the next control point.因此最基本的伪代码示例使用前一个控制点和终点来计算下一个控制点。 ? represents unknowns which are not bound by constraints required to keep the lines parallel表示不受保持线平行所需的约束的未知数

 // 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, ?, ?, ?, ?)

In the below page:在以下页面中:
https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_canvas_beziercurveto https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_canvas_beziercurveto

You change the width and height of the canvas to 1000.您将画布的宽度和高度更改为 1000。

Then you replace the two lines between beginpath and stroke with the below code.然后用下面的代码替换 beginpath 和 stroke 之间的两行。

points=[
{x:0,  y:300},//0

{x:100,y:500},//1
{x:200,y:300},//2

{x:300,y:100},//3
{x:400,y:300},//4

{x:100,y:500},//5
{x:100,y:300},//6
];


ctx.rect(points[0].x-5, points[0].y-5, 10,10);



var smoother={};
smoother.x=((points[1].x-points[0].x)/10)+points[0].x;
smoother.y=((points[1].y-points[0].y)/10)+points[0].y;

ctx.rect(smoother.x-5, smoother.y-5, 10,10);
ctx.rect(points[1].x-5, points[1].y-5, 10,10);
ctx.rect(points[2].x-5, points[2].y-5, 10,10);
ctx.moveTo(points[0].x,points[0].y);
ctx.bezierCurveTo(
    smoother.x, smoother.y,
    points[1].x, points[1].y,
    points[2].x, points[2].y
    );




var smoother={};
var dx=(points[2].x-points[1].x);
var dy=(points[2].y-points[1].y);
var yperx=(dy/dx);
travel_x=dx;
travel_y=(dx*yperx);
smoother.x=points[2].x+travel_x/3;
smoother.y=points[2].y+travel_y/3;

ctx.rect(smoother.x-5, smoother.y-5, 10,10);
ctx.rect(points[3].x-5, points[3].y-5, 10,10);
ctx.rect(points[4].x-5, points[4].y-5, 10,10);
ctx.moveTo(points[2].x,points[2].y);
ctx.bezierCurveTo(
    smoother.x, smoother.y,
    points[3].x, points[3].y,
    points[4].x, points[4].y
    );





var smoother={};
var dx=(points[4].x-points[3].x);
var dy=(points[4].y-points[3].y);
var yperx=(dy/dx);
travel_x=dx;
travel_y=(dx*yperx);
smoother.x=points[4].x+travel_x/3;
smoother.y=points[4].y+travel_y/3;

ctx.rect(smoother.x-5, smoother.y-5, 10,10);
ctx.rect(points[5].x-5, points[5].y-5, 10,10);
ctx.rect(points[6].x-5, points[6].y-5, 10,10);
ctx.moveTo(points[4].x,points[4].y);
ctx.bezierCurveTo(
    smoother.x, smoother.y,
    points[5].x, points[5].y,
    points[6].x, points[6].y
    );

You can also run it here by pressing the run button:您也可以通过按运行按钮在此处运行它:
https://www.w3schools.com/code/tryit.asp?filename=GSP1RKBFHGGK https://www.w3schools.com/code/tryit.asp?filename=GSP1RKBFHGGK

At that you can manipulate the pixels in points[], and notice that the bezier curves always connect kinda smoothly.在那你可以操纵点 [] 中的像素,并注意贝塞尔曲线总是有点平滑地连接。

That's because in each new bezier curve, the system automatically makes the first bezier point, which only serves the role of smoothing the line.这是因为在每条新的贝塞尔曲线中,系统会自动制作第一个贝塞尔点,它只起到平滑线条的作用。 Which is basically just a point that continues in whatever direction the previous bezier was heading, for a little bit.这基本上只是在前一个贝塞尔曲线走向的任何方向上继续的一点。 The next pixel in the bezier is then an actual destination, which the given bezier curve then takes care of smoothing.贝塞尔曲线中的下一个像素就是一个实际的目的地,给定的贝塞尔曲线然后负责平滑处理。

There is the number 3 in there, it represents how quickly you want to start going in the actual direction.那里有数字 3,它代表你想要多快开始朝着实际方向前进。 If it's too large we start to too quickly head in the needed direction and the smoothness suffers.如果它太大,我们就会开始太快地朝着所需的方向前进,并且平滑度会受到影响。 If it's too small we are ignoring too much where the line needs to be going, in favor of smoothness.如果它太小,我们就会忽略太多线条需要去的地方,而倾向于平滑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM