HTML5画布 - 在圆上绘制线性渐变(色轮)

时间:2013-02-08 16:43:21

标签: javascript html5-canvas linear-gradients radial-gradients color-wheel

我正在尝试绘制一个圆形,而不是径向渐变,但是围绕圆形的线性渐变...基本上,我正在尝试创建一个色轮,它必须是动态的,因为颜色将是可定制......然而,我对如何解决这个问题感到困惑......

我以为我可以绘制自己的圆圈并对其进行着色,然后以更大的半径循环该过程以填充它。但事实证明,这不仅非常无效,而且非常错误......

这是我的第一次尝试:http://jsfiddle.net/gyFqX/1/ 我坚持使用该方法,但改为将其填充为圆上每个点的2x2平方。它可以很好地混合多达3种颜色,但是你会开始注意到它的失真。

无论如何,我已经继续努力了,这就是我现在所拥有的:http://jsfiddle.net/f3SQ2/

var ctx = $('#canvas')[0].getContext('2d'),
    points = [],
    thickness = 80;

for( var n = 0; n < thickness; n++ )
    rasterCircle( 200, 200, (50 + n) );

function fillPixels() {
    var size = points.length,
        colors = [ 
            hexToRgb( '#ff0000' ), // Red
            hexToRgb( '#ff00ff' ), // Magenta
            hexToRgb( '#0000ff' ), // Blue
            hexToRgb( '#00ffff' ), // Teal
            hexToRgb( '#00ff00' ), // Green
            hexToRgb( '#ffff00' ), // Yellow            
            hexToRgb( '#ff0000' ), // Red
        ],
        colorSpan = colors.length - 1;

    if ( colors.length > 0 ) {
        var lastPadding = size % colorSpan,
            stepSize = size / colorSpan,
            steps = null, 
            cursor = 0;

        for ( var index = 0; index < colorSpan; index++ ) {
            steps = Math.floor( ( index == colorSpan - 1 ) ? stepSize + lastPadding : stepSize );
            createGradient( colors[ index ], colors[ index + 1 ], steps, cursor );
            cursor += steps;
        }
    }

    function createGradient( start, end, steps, cursor ) {
        for ( var i = 0; i < steps; i++ ) {
            var r = Math.floor( start.r + ( i * ( end.r - start.r ) / steps ) ),
                g = Math.floor( start.g + ( i * ( end.g - start.g ) / steps ) ),
                b = Math.floor( start.b + ( i * ( end.b - start.b ) / steps ) );

            ctx.fillStyle = "rgba("+r+","+g+","+b+",1)";
            ctx.fillRect( points[cursor][0], points[cursor][1], 2, 2 );
            cursor++;
        }
    }

    points = [];
}

function setPixel( x, y ) {
    points.push( [ x, y ] );
}

function rasterCircle(x0, y0, radius) {
    var f = 1 - radius,
        ddF_x = 1,
        ddF_y = -2 * radius,
        x = 0,
        y = radius;

    setPixel(x0, y0 + radius);
    while(x < y) {
        if(f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;    
        setPixel(x0 - x, y0 - y);
    }

    var temp = [];
    f = 1 - radius,
    ddF_x = 1,
    ddF_y = -2 * radius,
    x = 0,
    y = radius;
    while(x < y) {
        if(f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;    
        temp.push( [x0 - y, y0 - x] );
    }
    temp.push( [x0 - radius, y0] );

    for(var i = temp.length - 1; i > 0; i--)
        setPixel( temp[i][0], temp[i][1] );

    fillPixels();
}

我想要完成的事情是这样的:http://img252.imageshack.us/img252/3826/spectrum.jpg

'亮度'(白色到黑色渐变)不是问题,因为我知道可以通过在绘制色谱后使用径向渐变来实现。但是,我很想知道如何绘制频谱本身。

我甚至认为我可以绘制一个线性的然后弯曲(变换)它,但是没有任何本地函数可以做到这一点并且处理诸如超出我的技能水平的东西。 : - /

2 个答案:

答案 0 :(得分:4)

检查出来:http://jsfiddle.net/f3SQ2/5/

var can = $('#canvas')[0],
    ctx = can.getContext('2d'),
    radius = 120,
    thickness = 80,
    p = {
        x: can.width,
        y: can.height
    },
    start = Math.PI,
    end = start + Math.PI / 2,
    step = Math.PI / 180,
    ang = 0,
    grad,
    r = 0,
    g = 0,
    b = 0,
    pct = 0;

ctx.translate(p.x, p.y);
for (ang = start; ang <= end; ang += step) {
    ctx.save();
    ctx.rotate(-ang);
    // linear gradient: black->current color->white
    grad = ctx.createLinearGradient(0, radius - thickness, 0, radius);
    grad.addColorStop(0, 'black');

    h = 360-(ang-start)/(end-start) * 360;
    s = '100%';
    l = '50%';

    grad.addColorStop(.5, 'hsl('+[h,s,l].join()+')');
    grad.addColorStop(1, 'white');
    ctx.fillStyle = grad;

    // the width of three for the rect prevents gaps in the arc
    ctx.fillRect(0, radius - thickness, 3, thickness);
    ctx.restore();
}

编辑:固定色谱。显然我们可以给它HSL值,不需要转换或杂乱的计算!

修改了一些内容以更好地处理扩展:http://jsfiddle.net/f3SQ2/6/

step = Math.PI / 360

ctx.fillRect(0, radius - thickness, radius/10, thickness);

例如,您可以设置渐变停止:

h = 360-(ang-start)/(end-start) * 360;
s = '100%';

grad.addColorStop(0, 'hsl('+[h,s,'0%'].join()+')');  //black 
grad.addColorStop(.5,'hsl('+[h,s,'50%'].join()+')'); //color
grad.addColorStop(1, 'hsl('+[h,s,'100%'].join()+')');//white

答案 1 :(得分:1)

我的第一个注意事项是,您链接的图片包含所有3个不需要更改的组件,可能只是一个静态图像。

我从我正在处理的项目中调整了一些代码: http://jsfiddle.net/f3SQ2/1/

function drawColourArc(image) {
    var data = image.data;
    var i = 0;
    var w = image.width, h = image.height;
    var result = [0, 0, 0, 1];
    var outer = 1, inner = 0.5;
    var mid = 0.75;

    for (var y = 0; y < h; y++) {
        for (var x = 0; x < w; x++) {

            var dx = (x / w) - 1, dy = (y / w) - 1;

            var angular = ((Math.atan2(dy, dx) + Math.PI) / (2 * Math.PI)) * 4;
            var radius = Math.sqrt((dx * dx) + (dy * dy));

            if (radius < inner || radius > outer) {
                data[i++] = 255;
                data[i++] = 255;
                data[i++] = 255;
                data[i++] = 0;
            }
            else {
                if (radius < mid) {
                    var saturation = 1;
                    var brightness = (radius - 0.5) * 4;
                }
                else {
                    var saturation = 1- ((radius - 0.75) * 4);
                    var brightness = 1;
                }

                result[0] = angular;
                result[1] = saturation;
                result[2] = brightness;

                result[3] = 1;

                //Inline HSBToRGB
                if (result[1] == 0) {
                    result[0] = result[1] = result[2] = result[2];
                }
                else {
                    var varH = result[0] * 6;
                    var varI = Math.floor(varH); //Or ... var_i = floor( var_h )
                    var var1 = result[2] * (1 - result[1]);
                    var var2 = result[2] * (1 - result[1] * (varH - varI));
                    var var3 = result[2] * (1 - result[1] * (1 - (varH - varI)));

                    if (varI == 0 || varI == 6) {
                        result[0] = result[2];
                        result[1] = var3;
                        result[2] = var1;
                    }
                    else if (varI == 1) {
                        result[0] = var2;
                        result[1] = result[2];
                        result[2] = var1;
                    }
                    else if (varI == 2) {
                        result[0] = var1;
                        result[1] = result[2];
                        result[2] = var3;
                    }
                    else if (varI == 3) {
                        result[0] = var1;
                        result[1] = var2;
                        result[2] = result[2];
                    }
                    else if (varI == 4) {
                        result[0] = var3;
                        result[1] = var1;
                        result[2] = result[2];
                    }
                    else {
                        result[0] = result[2];
                        result[1] = var1;
                        result[2] = var2;
                    }

                }
                //End of inline
                data[i++] = result[0] * 255;
                data[i++] = result[1] * 255;
                data[i++] = result[2] * 255;
                data[i++] = result[3] * 255;
            }
        }
    }
};

var canvas = document.getElementsByTagName("canvas")[0];
var ctx = canvas.getContext("2d");
var image = ctx.createImageData(canvas.width, canvas.height);

drawColourArc(image);
ctx.putImageData(image, 0, 0);

这是每像素这是准确的,但你可能想绘制一个轮廓来对抗锯齿。它可以适应使用自定义颜色而不是插入色调。