如何使用循环和点绘制完美的多条线螺旋图?

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>

请看这里的图片描述 这里是图片描述

注意:当我改变增量值时,总会有一行比其他行多或少点数的情况。

increment = 5/32;

点击此处查看图片

点击此处查看图片

是否有可能画出一条完美的螺旋线,使得每条线段长度相等?

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,第5个点的距离为20,从那里开始,您已经打破了节奏。

假设您仍需要5个分支,在每5个点处通过检查count%5 === 0而不是count%4 === 0来增加距离。

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,则会得到五条直线。如果增量略大于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


我将增量更改为1.05/5,但仍然不平衡。 - Han Tran
你是不是使用了 count % 5 而不是 count % 4 - Chuanqi Sun
看一下,它是平衡的:https://jsbin.com/cejobuvanu/edit?html,output - Chuanqi Sun
抱歉,我真的看不出问题在哪里。是因为缺少一个点而变短了吗?我数了每个臂上确切的10个点。 - Chuanqi Sun
嗨,我发现了一张被认为是完美螺旋的图像,所有臂都具有相同的长度,我们能用代码画出类似的东西吗?imgur.com/RPy69Xo - Han Tran
显示剩余2条评论

1

圆的周长为2 * PI * 半径

我们可以用这个公式来确定在周长上覆盖一定距离所需的角度步长。因此,沿着曲线扫过给定距离的角度为距离 / 半径(请注意,这不是直线距离,而是沿曲线的距离)。

虽然你正在创建一个螺旋形,每个角度步长的距离都会稍微变长(因为线条向外移动),但这种近似对于人眼来说已经足够了。

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

  • 删除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>


嗨,机械臂仍然存在与我的相同的问题,我找到了一个拥有完美臂长的螺旋形,你知道如何像这样绘制它们吗?https://imgur.com/RPy69Xo - Han Tran

0

将代码更改为以数字形式递增后,您会发现“螺旋”并未按预期的行生成。

您需要计算出您想要的螺旋臂数量,并与停止限制一起计算。

递增值也从未被使用。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接