我如何绘制带有圈和点的螺旋完美倍数线?

问题描述 投票:2回答:3

我有一个代码可以绘制带点的螺旋线

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();
<canvas id="myCanvas" width="800" height="800" style="border:1px solid #c3c3c3;"></canvas>

enter image description hereenter image description here

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

increment = 5/32;

enter image description here

enter image description here

这是否有可能画出一条完美的螺旋线,并且所有线的长度都相同?

javascript loops canvas html5-canvas draw
3个回答
2
投票

这里有很多问题。就像@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();
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"></canvas>

您可以看到,前四个点的距离为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();
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"></canvas>

此外,一个完整的圆圈是2 * Math.PI。如果可以使用Math.cos((theta) * 2 * Math.PI * increment),则增量将成为每次绘制后点将移动的弧线。如果增量为1/5,则将获得5条直线。如果增量比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();
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"></canvas>

编辑

[与提问者聊天后,我意识到问题也必须与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();
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"></canvas>

enter image description here


0
投票

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

您将需要计算出想要的螺旋臂数量,并使用Stoplimit进行计算。

也从未使用过增量值。

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

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


0
投票

圆的周长为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();
  
<canvas id="myCanvas" width="800" height="800" style="border:1px solid #c3c3c3;"></canvas>

如果您想要单独的螺旋线,那么这是对上述内容的较小修改

  • 定义螺旋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);
}
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"></canvas>
© www.soinside.com 2019 - 2024. All rights reserved.