【问题标题】:How to remove gaps in gradient如何消除渐变中的间隙
【发布时间】:2014-10-24 04:07:37
【问题描述】:

渐变 6、7、8 和 10 在其初始颜色之后有明显的间隙。有没有办法填补这个空白?

我假设一旦范围被分区,如果整数分区的总和不能被画布的宽度整除,则它们的总和小于原始分区的总和。

信息

  • render1() - 通过在两种颜色之间生成一系列颜色来插值。

  • render2() - JavaScript 的 createLinearGradient() 有 n 个偶数停止。

$(document).ready(function () {
    function addCanvas(ol) {
        var canvas = $('<canvas>', {
            "width": 240,
                "height": 10
        }).addClass('grd')
        .appendTo($('<li>').appendTo(ol));
      return canvas[0];
    }
    var ol = $('<ol>').appendTo($('body'));
    var c1 = '#FF0000';
    var c2 = '#00FF00';
    var canvas;
    for (var i = 1; i <= 30; i+=3) {
        canvas = addCanvas(ol);
        var colors = generateColors(c1, c2, i);
        render(canvas, colors);
    }
    canvas = addCanvas(ol);
    render2(canvas, [c1, c2]);
});

function toHex(c) {
    return (function (hex) {
        return hex.length == 1 ? "0" + hex : hex;
    })(c.toString(16));
}

function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}

function rgbToHex(rgb) {
    var r = 0,
        g = 0,
        b = 0;
    if (arguments.length === 1) {
        r = rgb.r || 0;
        g = rgb.g || 0;
        b = rgb.b || 0;
    } else if (arguments.length === 3) {
        r = arguments[0];
        g = arguments[1];
        b = arguments[2];
    }
    return "#" + toHex(r) + toHex(g) + toHex(b);
}


function render(canvas, colors) {
    var ctx = canvas.getContext("2d");
    var n = colors.length;
    var x = 0;
    var subWidth = canvas.width / n;
    for (var i = 0; i < n; i++) {
        var start = i * subWidth;
        var end = start + subWidth;
        ctx.fillStyle = colors[i];
        ctx.fillRect(x + start, 0, x + end, canvas.height);
    }
}

function render2(canvas, colors) {
    var ctx = canvas.getContext("2d");
    var x = canvas.x;
    var y = canvas.y;
    var width = canvas.width;
    var height = canvas.height;
    ctx.fillStyle = createGradient(ctx, colors, x, y, width, height)
    ctx.fillRect(0, 0, width, height);
}

function generateColors(color1, color2, n) {
    function partition(a, b, n) {
        function magnitude(a, b) {
            if (a === b) return 0;
            else if (a > b) return -1 * (a - b);
            else return b - a;
        }
        return magnitude(a, b) / n;
    }
    var c1 = hexToRgb(color1);
    var c2 = hexToRgb(color2);
    var rd = partition(c1.r, c2.r, n);
    var gd = partition(c1.g, c2.g, n);
    var bd = partition(c1.b, c2.b, n);
    var colors = [];
    var i, r, g, b, h = n/2;
    for (i = 0; i < h; i++) {
        r = Math.floor(c1.r + rd * i);
        g = Math.floor(c1.g + gd * i);
        b = Math.floor(c1.b + bd * i);
        colors.push(rgbToHex(r, g, b));
    }
    for (var j = 0; i < n; j++, i++) {
        var k = h-j-1;
        r = Math.floor(c2.r - rd * k);
        g = Math.floor(c2.g - gd * k);
        b = Math.floor(c2.b - bd * k);
        colors.push(rgbToHex(r, g, b));
    }  
    return colors;
}

function createGradient(ctx, colors, x, y, width, height) {
    var h = height / 2;
    var grd = ctx.createLinearGradient(0, h, width, h);
    var n = Math.max(colors.length - 1, 1);
    for (var i = 0; i < colors.length; i++) {
        grd.addColorStop(i / n, colors[i]);
    }
    return grd;
}
canvas.grd {
  display: inline-block;
  margin: 1px 0;
}

ol {
  padding-left: 32px; 
  list-style-type:decimal;
}
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

【问题讨论】:

  • 我在 Chrome、Firefox 或 IE 11(都在 Windows 8.1 上)中看不到任何差距。您发现哪些平台和浏览器存在差距?
  • 他们很微弱:i.imgur.com/OWGrAUq.png
  • 我在 Linux 上看不到 Chrome 和 Firefox 的差距(甚至没有微弱的差距)

标签: javascript algorithm interpolation linear-gradients


【解决方案1】:

更新答案:新版本的渲染函数,将多余的像素分割并在每一侧放置大约一半:

function render(canvas, colors) {
    var ctx = canvas.getContext("2d");
    var n = colors.length;
    var r = canvas.width % n;
    var a = Math.floor(r/2), b = n - r + a;
    var subWidth = Math.floor(canvas.width / n) + 1;
    var start = 0;

    for (var i = 0; i < n; i++) {
        if (i == a) {
            subWidth -= 1;
        }
        if(i == b) {
            subWidth += 1;
        }
        ctx.fillStyle = colors[i];
        ctx.fillRect(start, 0, subWidth, canvas.height);

        start += subWidth;
    }
}

原答案

一些事情:

我认为导致微弱间隙的原因是某些矩形被绘制为包含小数像素的宽度。您可以做的是将矩形绘制为精确的像素宽度,其中一些比其他的宽一个像素。这是您的渲染函数的重写(我认为。这里已经很晚了:-))

function render(canvas, colors) {
    var ctx = canvas.getContext("2d");
    var n = colors.length;
    var r = canvas.width % n;
    var subWidth = Math.floor(canvas.width / n) + 1;
    var start = 0;

    for (var i = 0; i < n; i++) {
        if (i == r) {
            subWidth -= 1;
        }
        ctx.fillStyle = colors[i];
        ctx.fillRect(start, 0, subWidth, canvas.height);

        start += subWidth;
    }
}

这将计算适合画布宽度colors.length 次的最大整体宽度。然后它会计算出还有多少额外的像素(r),然后循环给前 r 个矩形 1 个额外的像素宽度。

(另外,请记住fillRect 函数的第三个参数是宽度,而不是结束。您的调用正在工作,因为矩形被绘制在彼此的顶部并且每个矩形的起始位置是正确的。)

我确信有一个更优雅的解决方案,但这个似乎工作!

另外,当我在玩这个时,我注意到在 javascript 中,canvas.width 是 300,而不是 240。这导致我四处搜索,我发现了这个很好的解释,说明它是如何在 SO 上工作的:Canvas width and height in HTML5 .我认为代码正在渲染成 300 x 150 的画布,然后缩小到 240 x 10。但最终,这并没有导致问题。

希望这会有所帮助!

【讨论】:

  • 谢谢!我更新了我的脚本并实现了interpolate() 函数:jsfiddle.net/MrPolywhirl/uzjtnvn3
  • 我今天写了这个,你可以传入任意数量的颜色 - JSFiddle
  • 好的,这太棒了,但我不禁对我对渲染函数的更新如何将所有额外的像素添加到左侧感到不满意。这是一个每边大约一半的版本:jsfiddle.net/5bwbgm56/4。另外,我认为我在回答中提到的缩放问题现在确实影响了这一点,所以我更改了 addCanvas 函数来设置它的宽度和高度。我认为这种方式的输出质量更好。这再次非常酷。如果你能告诉我,你打算用它做什么?或者是什么激发了这样做的灵感?
  • 哦,哇,既然你提到了它,它看起来有点锯齿状。感谢您的关注。我正在制作一个进度条/健康计,并决定添加一个非无缝渐变......这就是我所得到的。