使用 JavaScript 在 HTML 画布上绘制螺旋

Posted

技术标签:

【中文标题】使用 JavaScript 在 HTML 画布上绘制螺旋【英文标题】:Drawing a spiral on an HTML canvas using JavaScript 【发布时间】:2011-10-13 01:20:02 【问题描述】:

我已经搜索并没有找到关于如何使用 javascript 在画布中绘制螺旋的任何内容。

我认为使用贝塞尔曲线可以做到这一点,如果这不起作用,请使用lineTo(),但这似乎要困难得多。

另外,要做到这一点,我猜我必须使用三角学和极坐标绘图,而且我已经有一段时间没有这样做了。如果是这样的话,你能指出我在数学上的正确方向吗?

【问题讨论】:

什么样的螺旋?:en.wikipedia.org/wiki/Spiral 你有什么特别的螺旋吗? 我在想类似阿基米德螺旋的东西。最好我可以调整一些参数以获得一系列不同的螺旋。 【参考方案1】:

如果你有插画师,有一个很好的免费工具会有所帮助 ai2canvas

它将为您在 html canvas 标签中创建所有的 javascript 曲线!

(如果您正在寻找 archmedes 螺旋,那么您首先必须从 coreldraw 获取它并将其复制到 illustrator,因为默认的螺旋工具会扩大每个点的角度)

【讨论】:

【参考方案2】:

阿基米德螺线表示为r=a+b(angle)。将其转换为x,y坐标,将表示为x=(a+b*angle)*cos(angle)y=(a+b*angle)*sin(angle)。然后您可以将角度放入 for 循环中并执行以下操作:

for (i=0; i< 720; i++) 
  angle = 0.1 * i;
  x=(1+angle)*Math.cos(angle);
  y=(1+angle)*Math.sin(angle);
  context.lineTo(x, y);

请注意,以上假设 a = 1 和 b = 1。

这是一个 jsfiddle 链接:http://jsfiddle.net/jingshaochen/xJc7M/

【讨论】:

这行得通,除了要获得螺旋,您需要将增量设置为i+=.1 之类的东西,它看起来像多边形。 这把小提琴jsfiddle.net/pTymD 的轻微变化与速度的可变角度增量:var incr = angle ? 1/(a + b*angle) : 0.1;。这通过瞄准 1px 线长避免了低角度的过采样和高角度的欠采样。 你能展示一个修改过的jsfiddle吗?我无法执行您的建议。 @geoidesic 你的意思是什么修改? 抱歉,我的评论是@Phil H.【参考方案3】:

这是我曾经从 here 借来的 Java 螺旋的略微改动的 javascript 化版本

它使用lineTo(),并不那么难。

<!DOCTYPE HTML>
<html><body>
<canvas id="myCanvas"   style="border:1px solid #c3c3c3;"></canvas>
<script type="text/javascript">
    var c=document.getElementById("myCanvas");
    var cxt=c.getContext("2d");
    var centerX = 150;
    var centerY = 150;
    cxt.moveTo(centerX, centerY);

    var STEPS_PER_ROTATION = 60;
    var increment = 2*Math.PI/STEPS_PER_ROTATION;       
    var theta = increment;

    while( theta < 40*Math.PI) 
      var newX = centerX + theta * Math.cos(theta); 
      var newY = centerY + theta * Math.sin(theta); 
      cxt.lineTo(newX, newY);
      theta = theta + increment;
    
    cxt.stroke();
</script></body></html>

【讨论】:

【参考方案4】:

这是我为绘图Archimedean spirals写的一个函数:

CanvasRenderingContext2D.prototype.drawArchimedeanSpiral =
    CanvasRenderingContext2D.prototype.drawArchimedeanSpiral ||
        function(centerX, centerY, stepCount, loopCount,
                 innerDistance, loopSpacing, rotation)
        
            this.beginPath();

            var stepSize = 2 * Math.PI / stepCount,
                endAngle = 2 * Math.PI * loopCount,
                finished = false;

            for (var angle = 0; !finished; angle += stepSize) 
                // Ensure that the spiral finishes at the correct place,
                // avoiding any drift introduced by cumulative errors from
                // repeatedly adding floating point numbers.
                if (angle > endAngle) 
                    angle = endAngle;
                    finished = true;
                

                var scalar = innerDistance + loopSpacing * angle,
                    rotatedAngle = angle + rotation,
                    x = centerX + scalar * Math.cos(rotatedAngle),
                    y = centerY + scalar * Math.sin(rotatedAngle);

                this.lineTo(x, y);
            

            this.stroke();
        

【讨论】:

【参考方案5】:

这是使用以下函数绘制螺旋的示例:

spiral(ctx, 
  start: //starting point of spiral
    x: 200, 
    y: 200
  ,
  angle: 30 * (Math.PI / 180), //angle from starting point
  direction: false,
  radius: 100, //radius from starting point in direction of angle
  number: 3 // number of circles
);

螺旋图代码:

spiral = function(ctx,obj) 
  var center, eAngle, increment, newX, newY, progress, sAngle, tempTheta, theta;
  sAngle = Math.PI + obj.angle;
  eAngle = sAngle + Math.PI * 2 * obj.number;
  center = 
    x: obj.start.x + Math.cos(obj.angle) * obj.radius,
    y: obj.start.y + Math.sin(obj.angle) * obj.radius
  ;
  increment = 2 * Math.PI / 60/*steps per rotation*/;
  theta = sAngle;
  ctx.beginPath();
  ctx.moveTo(center.x, center.y);
  while (theta <= eAngle + increment) 
    progress = (theta - sAngle) / (eAngle - sAngle);
    tempTheta = obj.direction ? theta : -1 * (theta - 2 * obj.angle);
    newX = obj.radius * Math.cos(tempTheta) * progress;
    newY = obj.radius * Math.sin(tempTheta) * progress;
    theta += increment;
    ctx.lineTo(center.x + newX, center.y + newY);
  
  ctx.stroke();
;

【讨论】:

【参考方案6】:

以下代码将螺旋近似为四分之一圆的集合,每个圆的半径稍大。对于小转数,它可能看起来比阿基米德螺旋线更糟糕,但它应该运行得更快。

function drawSpiral(ctx, centerx, centery, innerRadius, outerRadius, turns=2, startAngle=0)
    ctx.save();
    ctx.translate(centerx, centery);
    ctx.rotate(startAngle);
    let r = innerRadius;
    let turns_ = Math.floor(turns*4)/4;
    let dr = (outerRadius - innerRadius)/turns_/4;
    let cx = 0, cy = 0; 
    let directionx = 0, directiony = -1;
    
    ctx.beginPath();
    let angle=0;
    for(; angle < turns_*2*Math.PI; angle += Math.PI/2)
        //draw a quarter arc around the center point (x, cy)
        ctx.arc( cx, cy, r, angle, angle + Math.PI/2);
        
        //move the center point and increase the radius so we can draw a bigger arc
        cx += directionx*dr;
        cy += directiony*dr;
        r+= dr;
        
        //rotate direction vector by 90 degrees
        [directionx, directiony] = [ - directiony, directionx ];
    
    //draw the remainder of the last quarter turn
    ctx.arc( cx, cy, r, angle, angle + 2*Math.PI*( turns - turns_ ))
    ctx.stroke();
    ctx.restore();

结果:

【讨论】:

以上是关于使用 JavaScript 在 HTML 画布上绘制螺旋的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JavaScript 在 html 画布内绘制图像以获取特殊过滤器

在 HTML5 画布上工作时出现 JavaScript (JQuery) 问题

使用 javascript 从 HTML5 画布镜像形状

在 HTML5 画布模式下使用带有外部 JavaScript 文件的 Adob​​e Animate CC

html 使用HTML5画布和JavaScript的简单模拟时钟。

canvas arc()方法