在两条线相交之间创建具有度数的曲线
Create a curve with degree between two lines intersection
我想像加拿大国旗一样制作枫叶,但是当线条相交时创建曲线时遇到问题,因此曲线仅在相交处并在线条之间产生一定的度数。
我的意思是在我在这里放的图片中的弧 A、B、C 等中显示:
这是我目前创建的函数:
function drawMapleLeaf (ctx,x,y,width,height) {
let rx = width;
let ry = height;
let xc = x + rx / 2;
let yc = y + height;
let xPoints = new Array(26).fill(0);
let yPoints = new Array(26).fill(0);
xPoints [0] = (xc + rx * 0.021423);
yPoints [0] = (yc - ry * 0.215686);
xPoints [1] = (xc + rx * 0.270780);
yPoints [1] = (yc - ry * 0.203804);
xPoints [2] = (xc + rx * 0.271820);
yPoints [2] = (yc - ry * 0.295752);
xPoints [3] = (xc + rx * 0.482015);
yPoints [3] = (yc - ry * 0.411765);
xPoints [4] = (xc + rx * 0.443046);
yPoints [4] = (yc - ry * 0.483267);
xPoints [5] = (xc + rx * 0.500000);
yPoints [5] = (yc - ry * 0.587435);
xPoints [6] = (xc + rx * 0.363353);
yPoints [6] = (yc - ry * 0.619576);
xPoints [7] = (xc + rx * 0.342287);
yPoints [7] = (yc - ry * 0.693849);
xPoints [8] = (xc + rx * 0.153596);
yPoints [8] = (yc - ry * 0.612537);
xPoints [9] = (xc + rx * 0.201601);
yPoints [9] = (yc - ry * 0.918462);
xPoints [10] = (xc + rx * 0.093001);
yPoints [10] = (yc - ry * 0.894514);
xPoints [11] = xc;
yPoints [11] = (yc - ry);
xPoints [12] = (xc - rx * 0.093001);
yPoints [12] = yPoints [10];
xPoints [13] = (xc - rx * 0.201601);
yPoints [13] = yPoints [9];
xPoints [14] = (xc - rx * 0.153596);
yPoints [14] = yPoints [8];
xPoints [15] = (xc - rx * 0.342287);
yPoints [15] = yPoints [7];
xPoints [16] = (xc - rx * 0.363353);
yPoints [16] = yPoints [6];
xPoints [17] = (xc - rx * 0.500000);
yPoints [17] = yPoints [5];
xPoints [18] = (xc - rx * 0.443046);
yPoints [18] = yPoints [4];
xPoints [19] = (xc - rx * 0.482015);
yPoints [19] = yPoints [3];
xPoints [20] = (xc - rx * 0.271820);
yPoints [20] = yPoints [2];
xPoints [21] = (xc - rx * .2707796);
yPoints [21] = yPoints [1];
xPoints [22] = (xc - rx * 0.021423);
yPoints [22] = yPoints [0];
xPoints [23] = xPoints [22];
yPoints [23] = yc;
xPoints [24] = xPoints [0];
yPoints [24] = yPoints [23];
xPoints [25] = xPoints [0];
yPoints [25] = yPoints [0];
ctx.beginPath();
ctx.moveTo(xPoints[0],yPoints[0]);
for(let i=1;i<xPoints.length;i++){
ctx.lineTo(xPoints[i],yPoints[i]);
}
//ctx.stroke();
ctx.fillStyle='red';
ctx.fill();
}
我会尽量使它变得简单,但可能需要一些尝试才能理解。 (最后一段中的最终代码)
先来看看如何arcTo()
works https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arcto-dev
您会注意到您有一个起点和两个控制点 (CP)。 CP1 是线相交的地方。 CP2 是第二行开始的地方。
话虽如此,如果我们取两条线,运行 在它们上面求一个相交公式,我们就可以得到 CP1。 CP2 将是第二行开始的地方。
在下面的代码片段中,第 1 行的顶部(第一个点)是叶子的顶部。 line2 的第一个点是第二片叶子的尖端。对于每一行,x2 和 y2 都是使用函数 getCoords()
导出的。由于您的图表具有不同的坐标系,因此您必须采用它给出的角度并将其从 360 中减去。因此 line1 的角度为 63 度,line2 的角度为 150 度。
只需插入数字,一旦获得 x2 和 y2 的 console.log()
,将它们复制并粘贴到线对象中。只要线条交叉,长度并不重要。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function getCoords(x1, y1, length, angle) {
let rads = angle * (Math.PI/180);
x2 = x1 + length * Math.cos(rads);
y2 = y1 + length * Math.sin(rads);
console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)
function drawLines(line1, line2) {
ctx.beginPath()
ctx.moveTo(line1.x1, line1.y1)
ctx.lineTo(line1.x2, line1.y2)
ctx.moveTo(line2.x1, line2.y1)
ctx.lineTo(line2.x2, line2.y2)
ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>
在两条线都有准确的 x1、y1、x2、y2 坐标后,我们就可以 运行 通过线相交函数得到 CP1。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function getCoords(x1, y1, length, angle) {
let rads = angle * (Math.PI/180);
x2 = x1 + length * Math.cos(rads);
y2 = y1 + length * Math.sin(rads);
console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)
function lineSegmentsIntersect(line1, line2) {
let a_dx = line1.x2 - line1.x1;
let a_dy = line1.y2 - line1.y1;
let b_dx = line2.x2 - line2.x1;
let b_dy = line2.y2 - line2.y1;
let s =
(-a_dy * (line1.x1 - line2.x1) + a_dx * (line1.y1 - line2.y1)) /
(-b_dx * a_dy + a_dx * b_dy);
let t =
(+b_dx * (line1.y1 - line2.y1) - b_dy * (line1.x1 - line2.x1)) /
(-b_dx * a_dy + a_dx * b_dy);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
console.log('control x: '+Math.round(line1.x1 + t * (line1.x2 - line1.x1)));
console.log('control y: '+Math.round( line1.y1 + t * (line1.y2 - line1.y1)));
}
}
//once both lines have acurate starting and end points plug them in here
//in the console you will see CP1 for acrTo()
lineSegmentsIntersect(line1, line2)
function drawLines(line1, line2) {
ctx.beginPath()
ctx.moveTo(line1.x1, line1.y1)
ctx.lineTo(line1.x2, line1.y2)
ctx.moveTo(line2.x1, line2.y1)
ctx.lineTo(line2.x2, line2.y2)
ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>
如您所见,该功能只是控制台记录坐标,供您在 arcTo()
中用作 CP1。现在你可以正确地画线了。请注意 arcTo()
将绘制一条直线连接到绘图中的前一个点。这意味着我们不需要使用'lineTo()'从上一个点到弧的起点,只需要从弧的终点到下一个点。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
//let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
//let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function drawLines() {
ctx.beginPath()
ctx.moveTo(200, 50) //Just a starting point
ctx.arcTo(236, 121, 275, 99, 13) //(control x, control y, start of line2 x, start of line2 y, radius)
ctx.lineTo(275, 99) //(start of line2)
ctx.stroke()
}
drawLines()
<canvas id='canvas'></canvas>
当你翻遍整片叶子后,最终的作品会是这样的。辅助函数已被删除,因为现在我已经有了数字,所以不需要它们。以这种方式创建绘图将有助于减少尝试填充时出现的问题。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
function drawShape() {
ctx.fillStyle = 'red';
ctx.beginPath()
ctx.moveTo(200, 50)
ctx.arcTo(243, 134, 275, 99, 13);
ctx.lineTo(275, 99)
ctx.arcTo(250, 226, 308, 164.5, 13)
ctx.lineTo(308, 164.5)
ctx.arcTo(321, 194, 380, 181.5, 13)
ctx.lineTo(380, 181.5)
ctx.arcTo(360, 244, 386, 256.5, 13)
ctx.lineTo(386, 256.5)
ctx.arcTo(288, 336, 301.5, 372, 13)
ctx.lineTo(301.5, 372)
ctx.arcTo(204, 355, 209, 453, 19)
ctx.lineTo(209, 453)
ctx.lineTo(191, 453)
//..you need to finish
ctx.closePath()
ctx.fill()
ctx.stroke()
}
drawShape()
<canvas id='canvas'></canvas>
我想像加拿大国旗一样制作枫叶,但是当线条相交时创建曲线时遇到问题,因此曲线仅在相交处并在线条之间产生一定的度数。 我的意思是在我在这里放的图片中的弧 A、B、C 等中显示:
这是我目前创建的函数:
function drawMapleLeaf (ctx,x,y,width,height) {
let rx = width;
let ry = height;
let xc = x + rx / 2;
let yc = y + height;
let xPoints = new Array(26).fill(0);
let yPoints = new Array(26).fill(0);
xPoints [0] = (xc + rx * 0.021423);
yPoints [0] = (yc - ry * 0.215686);
xPoints [1] = (xc + rx * 0.270780);
yPoints [1] = (yc - ry * 0.203804);
xPoints [2] = (xc + rx * 0.271820);
yPoints [2] = (yc - ry * 0.295752);
xPoints [3] = (xc + rx * 0.482015);
yPoints [3] = (yc - ry * 0.411765);
xPoints [4] = (xc + rx * 0.443046);
yPoints [4] = (yc - ry * 0.483267);
xPoints [5] = (xc + rx * 0.500000);
yPoints [5] = (yc - ry * 0.587435);
xPoints [6] = (xc + rx * 0.363353);
yPoints [6] = (yc - ry * 0.619576);
xPoints [7] = (xc + rx * 0.342287);
yPoints [7] = (yc - ry * 0.693849);
xPoints [8] = (xc + rx * 0.153596);
yPoints [8] = (yc - ry * 0.612537);
xPoints [9] = (xc + rx * 0.201601);
yPoints [9] = (yc - ry * 0.918462);
xPoints [10] = (xc + rx * 0.093001);
yPoints [10] = (yc - ry * 0.894514);
xPoints [11] = xc;
yPoints [11] = (yc - ry);
xPoints [12] = (xc - rx * 0.093001);
yPoints [12] = yPoints [10];
xPoints [13] = (xc - rx * 0.201601);
yPoints [13] = yPoints [9];
xPoints [14] = (xc - rx * 0.153596);
yPoints [14] = yPoints [8];
xPoints [15] = (xc - rx * 0.342287);
yPoints [15] = yPoints [7];
xPoints [16] = (xc - rx * 0.363353);
yPoints [16] = yPoints [6];
xPoints [17] = (xc - rx * 0.500000);
yPoints [17] = yPoints [5];
xPoints [18] = (xc - rx * 0.443046);
yPoints [18] = yPoints [4];
xPoints [19] = (xc - rx * 0.482015);
yPoints [19] = yPoints [3];
xPoints [20] = (xc - rx * 0.271820);
yPoints [20] = yPoints [2];
xPoints [21] = (xc - rx * .2707796);
yPoints [21] = yPoints [1];
xPoints [22] = (xc - rx * 0.021423);
yPoints [22] = yPoints [0];
xPoints [23] = xPoints [22];
yPoints [23] = yc;
xPoints [24] = xPoints [0];
yPoints [24] = yPoints [23];
xPoints [25] = xPoints [0];
yPoints [25] = yPoints [0];
ctx.beginPath();
ctx.moveTo(xPoints[0],yPoints[0]);
for(let i=1;i<xPoints.length;i++){
ctx.lineTo(xPoints[i],yPoints[i]);
}
//ctx.stroke();
ctx.fillStyle='red';
ctx.fill();
}
我会尽量使它变得简单,但可能需要一些尝试才能理解。 (最后一段中的最终代码)
先来看看如何arcTo()
works https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arcto-dev
您会注意到您有一个起点和两个控制点 (CP)。 CP1 是线相交的地方。 CP2 是第二行开始的地方。
话虽如此,如果我们取两条线,运行 在它们上面求一个相交公式,我们就可以得到 CP1。 CP2 将是第二行开始的地方。
在下面的代码片段中,第 1 行的顶部(第一个点)是叶子的顶部。 line2 的第一个点是第二片叶子的尖端。对于每一行,x2 和 y2 都是使用函数 getCoords()
导出的。由于您的图表具有不同的坐标系,因此您必须采用它给出的角度并将其从 360 中减去。因此 line1 的角度为 63 度,line2 的角度为 150 度。
只需插入数字,一旦获得 x2 和 y2 的 console.log()
,将它们复制并粘贴到线对象中。只要线条交叉,长度并不重要。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function getCoords(x1, y1, length, angle) {
let rads = angle * (Math.PI/180);
x2 = x1 + length * Math.cos(rads);
y2 = y1 + length * Math.sin(rads);
console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)
function drawLines(line1, line2) {
ctx.beginPath()
ctx.moveTo(line1.x1, line1.y1)
ctx.lineTo(line1.x2, line1.y2)
ctx.moveTo(line2.x1, line2.y1)
ctx.lineTo(line2.x2, line2.y2)
ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>
在两条线都有准确的 x1、y1、x2、y2 坐标后,我们就可以 运行 通过线相交函数得到 CP1。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function getCoords(x1, y1, length, angle) {
let rads = angle * (Math.PI/180);
x2 = x1 + length * Math.cos(rads);
y2 = y1 + length * Math.sin(rads);
console.log(x2, y2);
}
//line2 x, y, length, angle
getCoords(275, 99, 200, 150)
function lineSegmentsIntersect(line1, line2) {
let a_dx = line1.x2 - line1.x1;
let a_dy = line1.y2 - line1.y1;
let b_dx = line2.x2 - line2.x1;
let b_dy = line2.y2 - line2.y1;
let s =
(-a_dy * (line1.x1 - line2.x1) + a_dx * (line1.y1 - line2.y1)) /
(-b_dx * a_dy + a_dx * b_dy);
let t =
(+b_dx * (line1.y1 - line2.y1) - b_dy * (line1.x1 - line2.x1)) /
(-b_dx * a_dy + a_dx * b_dy);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
console.log('control x: '+Math.round(line1.x1 + t * (line1.x2 - line1.x1)));
console.log('control y: '+Math.round( line1.y1 + t * (line1.y2 - line1.y1)));
}
}
//once both lines have acurate starting and end points plug them in here
//in the console you will see CP1 for acrTo()
lineSegmentsIntersect(line1, line2)
function drawLines(line1, line2) {
ctx.beginPath()
ctx.moveTo(line1.x1, line1.y1)
ctx.lineTo(line1.x2, line1.y2)
ctx.moveTo(line2.x1, line2.y1)
ctx.lineTo(line2.x2, line2.y2)
ctx.stroke()
}
drawLines(line1, line2)
<canvas id='canvas'></canvas>
如您所见,该功能只是控制台记录坐标,供您在 arcTo()
中用作 CP1。现在你可以正确地画线了。请注意 arcTo()
将绘制一条直线连接到绘图中的前一个点。这意味着我们不需要使用'lineTo()'从上一个点到弧的起点,只需要从弧的终点到下一个点。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
//let line1 = {x1: 200, y1: 50, x2: 290.79809994790935, y2: 228.20130483767355}
//let line2 = {x1: 275, y1: 99, x2: 101.79491924311225, y2: 199}
function drawLines() {
ctx.beginPath()
ctx.moveTo(200, 50) //Just a starting point
ctx.arcTo(236, 121, 275, 99, 13) //(control x, control y, start of line2 x, start of line2 y, radius)
ctx.lineTo(275, 99) //(start of line2)
ctx.stroke()
}
drawLines()
<canvas id='canvas'></canvas>
当你翻遍整片叶子后,最终的作品会是这样的。辅助函数已被删除,因为现在我已经有了数字,所以不需要它们。以这种方式创建绘图将有助于减少尝试填充时出现的问题。
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
function drawShape() {
ctx.fillStyle = 'red';
ctx.beginPath()
ctx.moveTo(200, 50)
ctx.arcTo(243, 134, 275, 99, 13);
ctx.lineTo(275, 99)
ctx.arcTo(250, 226, 308, 164.5, 13)
ctx.lineTo(308, 164.5)
ctx.arcTo(321, 194, 380, 181.5, 13)
ctx.lineTo(380, 181.5)
ctx.arcTo(360, 244, 386, 256.5, 13)
ctx.lineTo(386, 256.5)
ctx.arcTo(288, 336, 301.5, 372, 13)
ctx.lineTo(301.5, 372)
ctx.arcTo(204, 355, 209, 453, 19)
ctx.lineTo(209, 453)
ctx.lineTo(191, 453)
//..you need to finish
ctx.closePath()
ctx.fill()
ctx.stroke()
}
drawShape()
<canvas id='canvas'></canvas>