如何使用HTML5画布生成彩虹圆?

5
我希望能以一种巧妙的方式使用渐变来生成画布图像。我希望该图像看起来像这样:rainbow circle。但是,我无法理解如何实现。我需要以弧形的形式生成线条,或者以某种聪明的方式使用颜色停止的渐变。也许,如果我将其转换为HSL并遍历HUE值,可能会更容易?例如,在矩形格式中,我可以...
for (var i = 0; i < h; ++i) {
  var ratio = i/h;
  var hue = Math.floor(360*ratio);
  var sat = 100;
  var lum = 50;
  line(dc, hslColor(hue,sat,lum), left_margin, top_margin+i, left_margin+w, top_margin+i);
}

有没有什么聪明的技巧,可以使用canvas来制作这张图片?
3个回答

16

这不是完美的(由于绘图步骤的缘故……),但它可以帮助你:

http://jsfiddle.net/afkLY/2/

HTML:

<canvas id="colors" width="200" height="200"></canvas>

Javascript:

var canvas = document.getElementById("colors");
var graphics = canvas.getContext("2d");

var CX = canvas.width / 2,
    CY = canvas.height/ 2,
    sx = CX,
    sy = CY;

for(var i = 0; i < 360; i+=0.1){
    var rad = i * (2*Math.PI) / 360;
    graphics.strokeStyle = "hsla("+i+", 100%, 50%, 1.0)";   
    graphics.beginPath();
    graphics.moveTo(CX, CY);
    graphics.lineTo(CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
    graphics.stroke();
}

这个想法是通过一行行绘制圆盘,并使用与线条方向对应的色调值来画出它。

您可以通过将半径角度添加到rad 变量中来更改颜色基础旋转(将-pi/2添加到 rad 会使渐变看起来像您的图像)。

编辑: 我创建了一个新的演示,稍微概括了一下这个概念并呈现了一个彩虹多边形。 这是CodePen 。 为了消除颜色之间的小空隙,我使用了四边形溢出到下一个颜色部分,除了最后一个。


在这种情况下是这样的。但步骤必须是光盘大小的函数,否则绘图不完整。让我们尝试使用2000x2000的画布大小而不更改代码:http://jsfiddle.net/afkLY/4/ 这是一个极端情况,但无论如何都是一个情况。 - dooxe
这看起来非常棒!谢谢,你能更详细地解释一下你所说的绘图步骤吗?你是指步骤必须是盘片尺寸的函数吗? - Yonder
一个“真实”的圆盘有无限多的半径(或直径),这就是为什么它完全填满了。当你想要在数字上绘制它时,你不能画出无限多的线来填充它。你必须选择一个足够准确的绘图分辨率(步骤)来完全填充圆盘。 - dooxe
是的 - 它肯定需要量化 - 更改步进值(0.1)会引入有趣的伪影。它肯定需要根据圆盘大小进行变量化。您是如何决定使用 0.1 的?我该如何计算不会引入与圆盘直径大小相关的伪影的最大可能步长? - Yonder
1
我们在距离半径处看到高度为1的角度是arctan(1/radius),这可以很好地近似为1/radius。请注意,你想要绘制的是饼图而不是线条,以避免在中心附近出现奇怪的旋转效果。将饼图近似为三角形对于小的饼图已经足够好了。对于更大的饼图,你需要创建可见的阴影。fiddle在这里:http://jsfiddle.net/gamealchemist/afkLY/11/http://jsfiddle.net/gamealchemist/afkLY/11/ - GameAlchemist
显示剩余2条评论

1

进行微调以使其具有白色中心

var canvas = document.getElementById('colorPicker'); var graphics = canvas.getContext("2d");

        var CX = canvas.width / 2,
            CY = canvas.height / 2,
            sx = CX,
            sy = CY;

        for (var i = 0; i < 360; i += 0.1) {
            var rad = i * (2 * Math.PI) / 360;
            var grad = graphics.createLinearGradient(CX, CY, CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
            grad.addColorStop(0, "white");
            grad.addColorStop(0.01, "white");
            grad.addColorStop(0.99, "hsla(" + i + ", 100%, 50%, 1.0)");
            grad.addColorStop(1, "hsla(" + i + ", 100%, 50%, 1.0)");
            graphics.strokeStyle = grad;
            graphics.beginPath();
            graphics.moveTo(CX, CY);
            graphics.lineTo(CX + sx * Math.cos(rad), CY + sy * Math.sin(rad));
            graphics.stroke();
        }

0
这里提供另一种方法,采用稍微更加函数式的方式:
var canvas = document.getElementById("radial"),
    ctx = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height,
    center = { x: width/2, y: height/2 },
    diameter = Math.min(width, height);

var distanceBetween = function(x1,y1,x2,y2) {
  // Get deltas
  var deltaX = x2 - x1,
      deltaY = y2 - y1;

  // Calculate distance from center
  return Math.sqrt(deltaX*deltaX+deltaY*deltaY);  
}

var angleBetween = function(x1,y1,x2,y2) {
  // Get deltas
  var deltaX = x2 - x1,
      deltaY = y2 - y1;

  // Calculate angle
  return Math.atan2(deltaY, deltaX);
}

var radiansToDegrees = _.memoize(function(radians) {
    // Put in range of [0,2PI)
  if (radians < 0) radians += Math.PI * 2;

  // convert to degrees
  return radians * 180 / Math.PI; 
})

// Partial application of center (x,y)
var distanceFromCenter = _.bind(distanceBetween, undefined, center.x, center.y)
var angleFromCenter = _.bind(angleBetween, undefined, center.x, center.y)

// Color formatters
var hslFormatter = function(h,s,l) { return "hsl("+h+","+s+"%,"+l+"%)"; },
    fromHue = function(h) { return hslFormatter(h,100,50); };

// (x,y) => color
var getColor = function(x,y) {
  // If distance is greater than radius, return black
  return (distanceFromCenter(x,y) > diameter/2)
    // Return black
    ? "#000"
    // Determine color
    : fromHue(radiansToDegrees(angleFromCenter(x,y)));
};

for(var y=0;y<height;y++) {
  for(var x=0;x<width;x++) {
    ctx.fillStyle = getColor(x,y);
    ctx.fillRect( x, y, 1, 1 );
  }
}

它使用函数来计算每个像素的颜色 - 这不是最有效的实现方式,但也许你可以从中获得一些有用的东西。
请注意,它使用underscore作为一些辅助函数,如bind() - 用于部分应用程序 - 和memoizeCodepen用于实验。

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