如何解决如何使用HTML5 Canvas平滑连续地连接2条Bezier曲线
我正在尝试将两条单独的贝塞尔曲线合并为一条连续曲线。目前,我的状态如下:
问题在于它们没有连接在一起,因此它们相遇的点看上去很尖锐/尖锐,而不是弯曲而平滑的。我已经研究了在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,canvas.width,canvas.height);
face = new Face();
draw();
};
function draw() {
setBackground(`rgba(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,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.hsy);
c.stroke();
c.fillStyle = (`rgba(25,211,0)`);
c.fill();
}
function setBackground(color) {
c.fillStyle = color;
c.fillRect(0,width,height);
}
function Face() {
this.hsx = 150;
this.hsy = 0;
this.hsy2 = 120;
this.hcp1x = 120;
this.hcp1y = 250;
}
解决方法
公共切线
要平滑地连接两个贝塞尔曲线,您需要使从公共点开始的线平行,从而将两个贝塞尔曲线的末端和起点的切线定义为相同。
下图说明了这一点
由两个控制点(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,?)
,
在以下页面:
https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_canvas_beziercurveto
您将画布的宽度和高度更改为 1000。
然后用下面的代码替换 beginpath 和 stroke 之间的两行。
points=[
{x:0,y:300},//0
{x:100,y:500},//1
{x:200,//2
{x:300,y:100},//3
{x:400,//4
{x:100,//5
{x:100,//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);
ctx.rect(points[1].x-5,points[1].y-5,10);
ctx.rect(points[2].x-5,points[2].y-5,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,10);
ctx.rect(points[3].x-5,points[3].y-5,10);
ctx.rect(points[4].x-5,points[4].y-5,10);
ctx.moveTo(points[2].x,points[2].y);
ctx.bezierCurveTo(
smoother.x,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,10);
ctx.rect(points[5].x-5,points[5].y-5,10);
ctx.rect(points[6].x-5,points[6].y-5,10);
ctx.moveTo(points[4].x,points[4].y);
ctx.bezierCurveTo(
smoother.x,points[5].x,points[5].y,points[6].x,points[6].y
);
您也可以在此处按运行按钮运行它:
https://www.w3schools.com/code/tryit.asp?filename=GSP1RKBFHGGK
您可以在点 [] 中操作像素,并注意贝塞尔曲线始终连接得比较平滑。
那是因为在每一条新的贝塞尔曲线中,系统都会自动做第一个贝塞尔点,它只起到平滑线条的作用。这基本上只是在前一个贝塞尔曲线走向的任何方向上继续的一点。贝塞尔曲线中的下一个像素就是实际的目的地,然后给定的贝塞尔曲线负责平滑处理。
里面有数字 3,它代表你想要多快开始朝着实际方向前进。如果它太大,我们就会开始太快地朝着所需的方向前进,并且平滑度会受到影响。 如果它太小,我们就会过多地忽略线条需要去的地方,而是为了平滑。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。