【问题标题】:How can i draw a perfect multiples lines spiral with loop & points?我怎样才能画出完美的多重线螺旋与循环和点?
【发布时间】:2019-11-21 04:51:16
【问题描述】:

我有一个用点画螺旋线的代码

var c = document.getElementById("myCanvas");
var cxt = c.getContext("2d");
var centerX = 400;
var centerY = 400;
cxt.moveTo(centerX, centerY);

var count = 0;
var increment = 3/32;
var distance = 10;

for (theta = 0; theta < 50; theta++) {
  var newX = centerX + distance * Math.cos((theta) * 4 * Math.PI * increment );
  var newY = centerY + distance * Math.sin(((theta)) * 4 * Math.PI * increment );
  cxt.fillText("o", newX, newY);
  count++;
  if (count % 4 === 0) {
    distance = distance + 10;
  }

}
cxt.stroke();
&lt;canvas id="myCanvas" width="800" height="800" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

enter image description here

注意我改变增量值多少次,总有一条线比其他线有更多或更少的点

increment = 5/32;

enter image description here

这是否可以绘制一个所有线都具有相同长度的完美螺旋?

【问题讨论】:

    标签: javascript loops canvas html5-canvas draw


    【解决方案1】:

    这里有很多问题。就像@Anytech 说的那样,您首先需要确定想要多少条手臂(点串)。在您的屏幕截图中,看起来您有 5 个手臂,但您可能是偶然得到的。我已将“o”替换为距离以帮助可视化问题:

    var c = document.getElementById("myCanvas");
    var cxt = c.getContext("2d");
    var centerX = 200;
    var centerY = 200;
    cxt.moveTo(centerX, centerY);
    
    var count = 0;
    var increment = 3/32;
    var distance = 10;
    
    for (theta = 0; theta < 50; theta++) {
      var newX = centerX + distance * Math.cos((theta) * 4 * Math.PI * increment );
      var newY = centerY + distance * Math.sin(((theta)) * 4 * Math.PI * increment );
      cxt.fillText(distance, newX, newY);
      count++;
      if (count % 4 === 0) {
        distance = distance + 10;
      }
    
    }
    cxt.stroke();
    &lt;canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    如你所见,前四个点在距离 10,第五个在距离 20,从那里开始,你已经打破了节奏。

    假设您仍然需要 5 个手臂,通过检查 count % 5 === 0 而不是 count % 4 === 0,每 5 个点增加一次距离。

    var c = document.getElementById("myCanvas");
    var cxt = c.getContext("2d");
    var centerX = 200;
    var centerY = 200;
    cxt.moveTo(centerX, centerY);
    
    var count = 0;
    var increment = 3/32;
    var distance = 10;
    
    for (theta = 0; theta < 50; theta++) {
      var newX = centerX + distance * Math.cos((theta) * 4 * Math.PI * increment );
      var newY = centerY + distance * Math.sin(((theta)) * 4 * Math.PI * increment );
      cxt.fillText(distance, newX, newY);
      count++;
      if (count % 5 === 0) {
        distance = distance + 10;
      }
    
    }
    cxt.stroke();
    &lt;canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    另外,一个完整的圆圈是2 * Math.PI。如果您可以使用Math.cos((theta) * 2 * Math.PI * increment),则增量变为每次绘制后点将移动的弧线。如果增量为1/5,您将得到五条直线。如果increment比1/5多一点,就会得到你想要的曲线效果。

    最后一个细节,我们通常将上下文称为ctx,而不是cxt。结合以上所有,输出如下所示

    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    var centerX = 200;
    var centerY = 200;
    ctx.moveTo(centerX, centerY);
    
    var count = 0;
    var increment = 1.02/5;
    var distance = 10;
    
    for (theta = 0; theta < 50; theta++) {
      var newX = centerX + distance * Math.cos((theta) * 2 * Math.PI * increment );
      var newY = centerY + distance * Math.sin(((theta)) * 2 * Math.PI * increment );
      ctx.fillText('o', newX, newY);
      count++;
      if (count % 5 === 0) {
        distance = distance + 10;
      }
    
    }
    ctx.stroke();
    &lt;canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    编辑

    在与提问者交谈后,我意识到问题也与fillText 使用字符串的左下角作为绘画的起点有关。为了解决这个问题,我们必须绘制实际的圆圈,而不是字母“o”。

    这是最终结果(添加同心圆以显示完美对称)

    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    var centerX = 200;
    var centerY = 200;
    ctx.moveTo(centerX, centerY);
    
    var count = 0;
    var increment = 1.05/5;
    var distance = 10;
    
    for (theta = 0; theta < 50; theta++) {
      var newX = centerX + distance * Math.cos((theta) * 2 * Math.PI * increment );
      var newY = centerY + distance * Math.sin(((theta)) * 2 * Math.PI * increment );
      ctx.textAlign = "center";
      //ctx.fillText('o', newX, newY); <== this will be off-center
      ctx.beginPath();
      ctx.strokeStyle = "#000";
      ctx.arc(newX, newY, 2, 0, Math.PI * 2, true)
      ctx.stroke();
      count++;
      if (count % 5 === 0) {
        ctx.strokeStyle = "#cccccc";
        ctx.beginPath();
        ctx.arc(200, 200, distance, 0, Math.PI * 2, true)
        ctx.stroke();
        distance = distance + 10;
      }
    
    }
    ctx.stroke();
    &lt;canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    【讨论】:

    • 我把增量改成1.05/5,还是不平衡
    • 你用count % 5代替count % 4吗?
    • 左下分支比其他分支短,将数字改为“o”,你会看到更容易
    • 对不起,我真的看不到这个问题。少了一个点会更短吗?我可以准确地数出每只手臂的 10 个点。
    【解决方案2】:

    圆的周长是2 * PI * radius

    我们可以使用它来确定在圆周上走一段距离所需要的角度步长。因此,沿曲线扫过给定距离的角度是distance / radius(注意这不是直线距离,而是沿曲线的距离)

    虽然您正在创建一个螺旋并且角度步长的距离稍长(随着线向外移动),但对于人眼来说,近似值就足够了。

    因此,请按照以下步骤更改您的代码

    • 删除increment
    • distance 更改为radius
    • 我们将使用半径来限制螺旋的最大半径,而不是限制圈数。添加maxRadius = 350 以适应画布
    • 添加lineLength 以设置每个点之间的近似距离(以像素为单位)。
    • 将 for 循环更改为 while 循环,因为我们将在循环内步进角度。
    • theta 重命名为angle(我们是程序员而不是数学家)
    • 我们将创建一条弧线路径,以便精确定位,而不是使用字符来绘制每个点。这将添加一些 2D 上下文设置代码。
    • 我们将半径作为角度的函数,而不是每 4 个点退出一次(因为这不再有效)。添加一些常量来控制半径函数。
    • radiusMin 定义最小半径(起始半径)
    • radiusScale 定义螺旋移出的每圈像素速率。
    • 删除不再需要的count
    • 在循环内部计算半径。由于我们将半径增长定义为每转的速率,我们将radiusScale / (Math.PI * 2) 相除,因此半径为radius = angle * (radiusScale / (Math.PI * 2)) + radiusMin;
    • 在循环内部,我们调整角度以匹配我们想要移动的距离angle += lineLength / radius;,该距离源自圆周公式(以及为什么我们使用弧度表示角度)
    • cxt 更改为更惯用的ctx
    • 添加moveTo 以移动到每个圈子的开头。
    • 添加ctx.arc 定义圆
    • 当所有的圆都定义好后,循环后用ctx.stroke()绘制路径

    下面的代码。由于我只近似了您的螺旋,您可以使用常量来满足您的需求。另请注意,对于内螺旋,较长的线距也不起作用。

    const ctx = myCanvas.getContext("2d");
    const centerX = 400;
    const centerY = 400;
    
    const markRadius = 2;   // radius of each circle mark in pixels
    const maxRadius = 350;  // 50 pixel boarder
    const lineLength = 20;  // distance between points in pixels 
    const radiusScale = 80; // how fast the spiral moves outward per turn 
    const radiusMin = 10;   // start radius
    
    var angle = 0, radius = 0;
    
    ctx.lineWidth = 1;
    ctx.strokeStye = "black";
    ctx.beginPath();
    
    while (radius < maxRadius) {
      radius = angle * (radiusScale / (Math.PI * 2)) + radiusMin;
      angle += lineLength / radius;
      const x = centerX + radius * Math.cos(angle);
      const y = centerY + radius * Math.sin(angle);
      ctx.moveTo(x + markRadius, y);
      ctx.arc(x, y, markRadius, 0, Math.PI * 2);
    }
    ctx.stroke();
      
    &lt;canvas id="myCanvas" width="800" height="800" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    如果你想要单独的螺旋,那么它是上面的一个小修改

    • 定义螺旋中的臂数armCount
    • 将手臂转动的速率定义为移出“spiralRate”
    • 只是为了好玩动画spiralRate

    requestAnimationFrame(mainLoop);
    const ctx = myCanvas.getContext("2d");
    const centerX = 200;
    const centerY = 200;
    
    const markRadius = 2;   // radius of each circle mark in pixels
    const maxRadius = 190;  // 50 pixel boarder
    const armCount = 8;  // Number of arms
    const radiusScale = 8; // how fast the spiral moves outward per turn 
    const radiusMin = 10;   // start radius
    
    function drawSpiral(spiralRate) { // spiralRate in pixels per point per turn
      var angle = 0, radius = radiusMin;
    
      ctx.lineWidth = 1;
      ctx.strokeStye = "black";
      ctx.beginPath();
    
    
      while (radius < maxRadius) {
        angle += (Math.PI * 2) / armCount + (spiralRate/ radius);
        radius = angle * (radiusScale / (Math.PI * 2)) + radiusMin;
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);
        ctx.moveTo(x + markRadius, y);
        ctx.arc(x, y, markRadius, 0, Math.PI * 2);
      }
      
      ctx.stroke();
    }
    
    
    function mainLoop(time) {
       ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
       drawSpiral(Math.sin(time / 4000) * 2); // occilate spiral rate every ~ 24 seconds
       requestAnimationFrame(mainLoop);
    }
    &lt;canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"&gt;&lt;/canvas&gt;

    【讨论】:

    • 你好,手臂还是和我一样的问题,我找到了一个完美手臂长度的螺旋,你知道怎么画成这样吗? imgur.com/RPy69Xo
    【解决方案3】:

    将代码更改为数字形式的增量后,您可以看到预期行中没有生成“螺旋”。

    你需要计算出你想要多少个旋臂,并用停止限制计算出来。

    增量值也从未使用过。

    // var increment = 6/45; 
    var stoplimit = 51;  
    

    https://jsfiddle.net/Anytech/spzyufev/4/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-05
      • 1970-01-01
      • 1970-01-01
      • 2021-07-14
      • 2013-08-18
      • 2021-09-27
      • 2017-06-25
      • 1970-01-01
      相关资源
      最近更新 更多