我正在尝试从给定的弧(二次贝塞尔曲线)计算出2个同心圆弧(立方贝塞尔曲线)。我想我可以计算1/3和2/3的立方体的控制点,但它并不完全匹配。
var u = 1 / 3; // fraction of curve where Px1 and Py1 are
var v = 2 / 3; // fraction of curve where Px2 and Py2 are
//Calculate control points (Cx1, Cy1, Cx2, Cy2)
var a = 3 * (1 - u) * (1 - u) * u;
var b = 3 * (1 - u) * u * u;
var c = 3 * (1 - v) * (1 - v) * v;
var d = 3 * (1 - v) * v * v;
var det = a * d - b * c;
var Qx1 = Px1 - ((1 - u) * (1 - u) * (1 - u) * Px0 + u * u * u * Px3);
var Qy1 = Py1 - ((1 - u) * (1 - u) * (1 - u) * Py0 + u * u * u * Py3);
var Qx2 = Px2 - ((1 - v) * (1 - v) * (1 - v) * Px0 + v * v * v * Px3);
var Qy2 = Py2 - ((1 - v) * (1 - v) * (1 - v) * Py0 + v * v * v * Py3);
var Cx1 = (d * Qx1 - b * Qx2) / det;
var Cy1 = (d * Qy1 - b * Qy2) / det;
var Cx2 = ((-c) * Qx1 + a * Qx2) / det;
var Cy2 = ((-c) * Qy1 + a * Qy2) / det;
ctx.beginPath();
ctx.moveTo(Px0, Py0);
ctx.bezierCurveTo(Cx1, Cy1, Cx2, Cy2, Px3, Py3);
ctx.strokeStyle = "#0000FF";
ctx.stroke();
控制点是否也取决于弧的半径或完全不同的东西?立方贝塞尔甚至是绘制同心圆弧的好选择吗?二次贝塞尔绝对不起作用,立方体肯定让我更接近我需要的东西。
这是链接: http://codepen.io/davidreed0/full/zGqPxQ/
使用位置滑块移动椭圆。
答案 0 :(得分:0)
现在的问题是对要求有点不清楚。在任何情况下,这都是一种不需要太多计算的方法,但是利用绘制操作可视化与codepen中显示的相同。
主要步骤是:
可以扩展半径/直径。如果你需要可变半径,你可以使用贝塞尔公式将一系列蓝色弧线相互叠加。
这将逐步显示该过程。
在屏幕外的画布上(此处显示在屏幕上进行演示,我们将在下一步中切换):
var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
dia = 90; // diameter of graphics
ctx.strokeStyle = "blue"; // color
ctx.lineWidth = dia; // line-width = dia
ctx.lineCap = "round"; // round caps
// draw bezier (quadratic, one control point)
ctx.moveTo(dia, dia);
ctx.quadraticCurveTo(300, 230, c.width - dia, dia);
ctx.stroke();
<canvas width=600 height=300></canvas>
完成。我们现在有了主要的形状。根据需要调整点数。
由于我们现在有主要形状,我们将使用这个形状创建轮廓:
var c = document.querySelector("canvas"),
co = document.createElement("canvas"),
ctx = c.getContext("2d"),
ctxo = co.getContext("2d"),
dia = 90;
co.width = c.width;
co.height = c.height;
ctxo.strokeStyle = "blue";
ctxo.lineWidth = dia;
ctxo.lineCap = "round";
// draw bezier (quadratic here, one control point)
ctxo.moveTo(dia, dia);
ctxo.quadraticCurveTo(300, 230, c.width - dia, dia);
ctxo.stroke();
// draw multiple times to main canvas
var thickness = 1, angle = 0, step = Math.PI * 0.25;
for(; angle < Math.PI * 2; angle += step) {
var x = thickness * Math.cos(angle),
y = thickness * Math.sin(angle);
ctx.drawImage(co, x, y);
}
// punch out center
ctx.globalCompositeOperation = "destination-out";
ctx.drawImage(co, 0, 0);
<canvas width=600 height=300></canvas>
使用画布的自定义实现绘制绿色圆圈。首先,我们备份生成的蓝色轮廓的副本,以便我们可以在自由圆的顶部重绘它。我们可以重复使用离线画布,只需清除它并绘制结果(重置变换):
我们需要的唯一计算是二次贝塞尔曲线,我们在[0,1]范围内提供t
得到一个点:
function getQuadraticPoint(z0x, z0y, cx, cy, z1x, z1y, t) {
var t1 = (1 - t), // (1 - t)
t12 = t1 * t1, // (1 - t) ^ 2
t2 = t * t, // t ^ 2
t21tt = 2 * t1 * t; // 2(1-t)t
return {
x: t12 * z0x + t21tt * cx + t2 * z1x,
y: t12 * z0y + t21tt * cy + t2 * z1y
}
}
结果将是(使用更接近原始codepen的值):
var c = document.querySelector("canvas"),
co = document.createElement("canvas"),
ctx = c.getContext("2d"),
ctxo = co.getContext("2d"),
radius = 150,
dia = radius * 2;
co.width = c.width;
co.height = c.height;
ctxo.translate(2,2); // to avoid clipping of edges in this demo
ctxo.strokeStyle = "blue";
ctxo.lineWidth = dia;
ctxo.lineCap = "round";
// draw bezier (quadratic here, one control point)
ctxo.moveTo(radius, radius);
ctxo.quadraticCurveTo(300, 230, c.width - radius - 6, radius);
ctxo.stroke();
// draw multiple times to main canvas
var thickness = 1, angle = 0, step = Math.PI * 0.25;
for(; angle < Math.PI * 2; angle += step) {
var x = thickness * Math.cos(angle),
y = thickness * Math.sin(angle);
ctx.drawImage(co, x, y);
}
// punch out center
ctx.globalCompositeOperation = "destination-out";
ctx.drawImage(co, 0, 0);
// back-up result by reusing off-screen canvas
ctxo.clearRect(0, 0, co.width, co.height);
ctxo.drawImage(c, 0, 0);
// Step 3: draw the green circle at any point
ctx.globalCompositeOperation = "source-over"; // normal comp. mode
ctx.fillStyle = "#9f9";
ctx.strokeStyle = "#090";
var t = 0, dlt = 0.01;
(function loop(){
ctx.clearRect(0, 0, c.width, c.height);
t += dlt;
// calc position based on t [0, 1] and the same points as for the blue
var pos = getQuadraticPoint(radius, radius, 300, 230, c.width - radius, radius, t);
// draw the arc
ctx.beginPath();
ctx.arc(pos.x + 2, pos.y + 2, radius, 0, 2*Math.PI);
ctx.fill();
// draw center line
ctx.beginPath();
ctx.moveTo(radius, radius);
ctx.quadraticCurveTo(300, 230, c.width - radius - 6, radius);
ctx.stroke();
// draw blue outline on top
ctx.drawImage(co, 0, 0);
if (t <0 || t >= 1) dlt = -dlt; // ping-pong for demo
requestAnimationFrame(loop);
})();
// formula for quadr. curve is: B(t) = (1-t)^2 * Z0 + 2(1-t)t * C + t^2 * Z1
function getQuadraticPoint(z0x, z0y, cx, cy, z1x, z1y, t) {
var t1 = (1 - t), // (1 - t)
t12 = t1 * t1, // (1 - t) ^ 2
t2 = t * t, // t ^ 2
t21tt = 2 * t1 * t; // 2(1-t)t
return {
x: t12 * z0x + t21tt * cx + t2 * z1x,
y: t12 * z0y + t21tt * cy + t2 * z1y
}
}
<canvas width=600 height=600></canvas>
var c = document.querySelector("canvas"),
co = document.createElement("canvas"),
ctx = c.getContext("2d"),
ctxo = co.getContext("2d"),
radius = 150,
dia = radius * 2;
co.width = c.width;
co.height = c.height;
ctxo.translate(2,2); // to avoid clipping of edges in this demo
ctxo.strokeStyle = "blue";
ctxo.lineWidth = dia;
ctxo.lineCap = "round";
// draw bezier (quadratic here, one control point)
ctxo.moveTo(radius, radius);
ctxo.quadraticCurveTo(300, 230, c.width - radius - 6, radius);
ctxo.stroke();
// draw multiple times to main canvas
var thickness = 2, angle = 0, step = Math.PI * 0.25;
for(; angle < Math.PI * 2; angle += step) {
var x = thickness * Math.cos(angle),
y = thickness * Math.sin(angle);
ctx.drawImage(co, x, y);
}
// punch out center
ctx.globalCompositeOperation = "destination-out";
ctx.drawImage(co, 0, 0);
// back-up result by reusing off-screen canvas
ctxo.setTransform(1,0,0,1,0,0); // remove scale
ctxo.clearRect(0, 0, co.width, co.height);
ctxo.drawImage(c, 0, 0);
// Step 3: draw the green circle at any point
ctx.globalCompositeOperation = "source-over"; // normal comp. mode
ctx.fillStyle = "#9f9";
ctx.strokeStyle = "#090";
ctx.scale(1, 0.4); // create ellipse
var t = 0, dlt = 0.01;
(function loop(){
ctx.clearRect(0, 0, c.width, c.height * 1 / 0.4);
t += dlt;
// calc position based on t [0, 1] and the same points as for the blue
var pos = getQuadraticPoint(radius, radius, 300, 230, c.width - radius, radius, t);
// draw the arc
ctx.beginPath();
ctx.arc(pos.x + 2, pos.y + 2, radius, 0, 2*Math.PI);
ctx.fill();
// draw center line
ctx.beginPath();
ctx.moveTo(radius, radius);
ctx.quadraticCurveTo(300, 230, c.width - radius - 6, radius);
ctx.stroke();
// draw blue outline on top
ctx.drawImage(co, 0, 0);
if (t <0 || t >= 1) dlt = -dlt; // ping-pong for demo
requestAnimationFrame(loop);
})();
// formula for quadr. curve is: B(t) = (1-t)^2 * Z0 + 2(1-t)t * C + t^2 * Z1
function getQuadraticPoint(z0x, z0y, cx, cy, z1x, z1y, t) {
var t1 = (1 - t), // (1 - t)
t12 = t1 * t1, // (1 - t) ^ 2
t2 = t * t, // t ^ 2
t21tt = 2 * t1 * t; // 2(1-t)t
return {
x: t12 * z0x + t21tt * cx + t2 * z1x,
y: t12 * z0y + t21tt * cy + t2 * z1y
}
}
<canvas width=600 height=600></canvas>