我正在尝试用画布中的二次曲线构建一个近乎完美的圆。我有这个功能,用于围绕圆圈设置点并用二次曲线连接它们:
function calcPointsCircle(cx, cy, radius, dashLength) {
var n = radius / dashLength,
alpha = Math.PI * 2 / n,
i = -1;
while (i < n) {
var theta = alpha * i,
theta2 = alpha * (i + 1);
points.push({
x : (Math.cos(theta) * radius) + cx,
y : (Math.sin(theta) * radius) + cy,
ex : (Math.cos(theta2) * radius) + cx,
ey : (Math.sin(theta2) * radius) + cy,
py : (Math.sin(theta) * radius) + cy
});
i+=2;
}
}
for (i = 0; i < points.length; i++) {
var p = points[i];
ctx.strokeStyle = '#fff';
ctx.quadraticCurveTo(p.x, p.py, p.x, p.y);
ctx.stroke();
}
它有效,但线条目前是直的(很明显,因为我使用的是控制点的x和y坐标):
我正在寻找一种方法来根据圆弧半径和点数自动计算控制点的位置...所有帮助都更受欢迎
答案 0 :(得分:2)
这里是如何计算一组二次曲线的控制点,这些二次曲线近似于一个包围正多边形的圆。
<强>假设:强>
中心点,半径和sidecount。
对于多边形的每一边,计算:
在外接圆周上的3个点,然后计算导致曲线通过这3个点的二次曲线控制点:
多边形的2个点是3个点中的2个
计算边的2个点之间的扫掠角(变速扫描)
将扫掠角度(扫描/ 2)
使用三角法计算边缘2点中间圆周上的点。
计算中间控制点:
// calc middle control point
var cpX=2*x1-x0/2-x2/2;
var cpY=2*y1-y0/2-y2/2;
示例代码和演示:
// change sideCount to the # of poly sides desired
//
var sideCount=5;
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineWidth=2;
ctx.fillStyle=randomColor();
// save PI*2
var PI2=Math.PI*2;
// functions to calc a point on circumference of circle
var xx=function(a){return(cx+radius*Math.cos(a));}
var yy=function(a){return(cy+radius*Math.sin(a));}
// general interpolation function
var lerp=function(a,b,x){ return(a+x*(b-a)); }
// define the regular polygon
var cx=150;
var cy=150;
var radius=100;
// calc qCurve controls points and put in sides[] array
var sides=[];
for(var i=0;i<sideCount;i++){
sides.push(makeSide(i,sideCount));
}
// drawing and animating stuff
var percent=0;
var percentDirection=0.50;
$("#toShape").click(function(){
percentDirection=-0.50;
})
$("#toCircle").click(function(){
percentDirection=0.50;
})
animate();
// functions
function animate(){
requestAnimationFrame(animate);
drawSides(percent);
percent+=percentDirection;
if(percent>100){percent=100;}
if(percent<0){percent=0;}
}
function drawSides(pct,color){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(pct==100){
ctx.beginPath();
ctx.arc(cx,cy,radius,0,PI2);
ctx.closePath();
ctx.fill();
}else{
ctx.beginPath();
ctx.moveTo(sides[0].x0,sides[0].y0);
for(var i=0;i<sideCount;i++){
var side=sides[i];
var cpx=lerp(side.midX,side.cpX,pct/100);
var cpy=lerp(side.midY,side.cpY,pct/100);
ctx.quadraticCurveTo(cpx,cpy,side.x2,side.y2);
}
ctx.fill();
}
}
// given a side of a regular polygon,
// calc a qCurve that approximates a circle
function makeSide(n,sideCount){
// starting & ending angles vs centerpoint
var sweep=PI2/sideCount;
var sAngle=sweep*(n-1);
var eAngle=sweep*n;
// given start & end points,
// calc the point on circumference at middle of sweep angle
var x0=xx(sAngle);
var y0=yy(sAngle);
var x1=xx((eAngle+sAngle)/2);
var y1=yy((eAngle+sAngle)/2);
var x2=xx(eAngle);
var y2=yy(eAngle);
// calc the control points to pass a qCurve
// through the 3 points
var dx=x2-x1;
var dy=y2-y1;
var a=Math.atan2(dy,dx);
var midX=lerp(x0,x2,0.50);
var midY=lerp(y0,y2,0.50);
// calc middle control point
var cpX=2*x1-x0/2-x2/2;
var cpY=2*y1-y0/2-y2/2;
return({
x0:x0, y0:y0,
x2:x2, y2:y2,
midX:midX, midY:midY,
cpX:cpX, cpY:cpY,
color:randomColor()
});
}
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
&#13;
body{ background-color: ivory; }
canvas{border:1px solid red;}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id="toShape">Animate to Shape</button>
<button id="toCircle">Animate to Circle</button><br>
<canvas id="canvas" width=300 height=300></canvas>
&#13;