【问题标题】:Building a circle with quadratic curves in canvas在画布中用二次曲线构建一个圆
【发布时间】:2015-01-12 20:49:05
【问题描述】:

我正在尝试在画布中用二次曲线构建一个近乎完美的圆。我有这个功能可以围绕一个圆设置点并将它们与二次曲线连接起来:

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 坐标作为控制点):

我正在寻找一种方法来根据圆半径和点数自动计算控制点的位置...欢迎所有帮助

【问题讨论】:

    标签: html animation canvas quadratic curves


    【解决方案1】:

    这是计算一组二次曲线的控制点的方法,这些二次曲线近似于一个外接正多边形的圆。

    鉴于:

    中心点、半径和边数。

    对于多边形的每一边,计算:

    外接圆周上的3个点,然后计算导致曲线通过这3个点的二次曲线控制点:

    • 多边形边的2个点是3个点中的2个

    • 计算边的 2 个点之间的扫角(var sweep)

    • 平分扫掠角 (sweep/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));
    }
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
    <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>

    【讨论】:

      猜你喜欢
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-18
      相关资源
      最近更新 更多