【问题标题】:How can I generate a rainbow circle using HTML5 canvas?如何使用 HTML5 画布生成彩虹圈?
【发布时间】:2013-11-15 09:41:09
【问题描述】:

我想以某种巧妙的方式使用渐变生成画布图像。我希望图像看起来像这样:

我就是想不通。我需要以形式和弧线生成线条 - 或以某种巧妙的方式使用带有色标的渐变。如果我转换为 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);
}

有人有关于如何使用画布制作此图像的任何巧妙技巧吗?

【问题讨论】:

    标签: javascript canvas rgb color-space hsl


    【解决方案1】:

    这并不完美(由于绘图步骤......),但它可以帮助你:

    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 的画布大小:jsfiddle.net/afkLY/4 这是一个极端情况,但无论如何都是一个情况。
    • 这看起来真的很棒!谢谢,你能详细说明一下你对绘图步骤的意思吗?您的意思是,步长必须是磁盘大小的函数?
    • 一个“真正的”圆盘有无限多的半径(或直径),这就是它被完全填满的原因。当您想以数字方式绘制它时,您无法绘制无限多的线条来填充它。您必须选择足够精确的绘图分辨率(步长)以完全填满光盘。
    • 是的 - 它肯定需要被量化 - 并且更改该步长值 (0.1) 将引入有趣的工件。它肯定需要根据光盘的大小而变化。你是如何决定 0.1 的?我如何计算不会引入与圆盘直径尺寸相关的人工制品的最大可能步长?
    • 我们在距离半径处看到高度为 1 的角度是 arctan(1/radius),它可以很好地近似为 1/radius。请注意,您要绘制的是饼图,而不是线条,以避免在中心附近出现这种奇怪的旋转效果。将馅饼近似为三角形对于小馅饼来说已经足够了。使用更大的馅饼,您可以创建可见的阴影。小提琴在这里:jsfiddle.net/gamealchemist/afkLY/11/http://jsfiddle.net/…
    【解决方案2】:

    小幅调整使其具有白色中心

    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();
            }
    

    【讨论】:

      【解决方案3】:

      这是一种替代方法,它采用了功能性更强的方法:

      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()(用于部分应用程序)和memoize

      Codepen 用于实验。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        • 1970-01-01
        • 2010-11-15
        • 2014-09-21
        相关资源
        最近更新 更多