如何创建像 Philips Hue 这样的颜色选择器圈

How to create a color picker circle like Phillips Hue

我正在尝试在 javascript canvas 中创建颜色选择器。我想做这样的事情:

我希望当我单击圆圈中的任意位置时,我得到 x、y 坐标(这样我可以使用其他元素来标记所选颜色)和颜色。 如果解决方案是 而不是带有嵌入图像 的 canvas,而是 linear-gradient.

我已经在互联网上搜索过,但没有找到我的问题。

P.S.: 也许css border-radius 会帮忙转圈

如果您真的不想使用图像,您目前需要手动绘制每个像素。我确定某处有数学函数。

您不能使用渐变来做到这一点的原因是,要获得这样的渐变色轮,您需要叠加径向渐变和圆锥渐变。圆锥渐变将创建色轮,径向渐变将提供从中心展开的白色叠加层。

您目前无法使用 canvas 执行此操作,因为并非所有浏览器 currently support createConicGradient 都在 canvas 上运行。在这些浏览器中,您只能使用径向渐变(当然还有线性渐变,但这些对此无济于事)。

可以 使用堆叠 CSS 背景渐变,但浏览器目前没有可靠的方法来确定特定 X/Y 下单个像素的背景颜色坐标,canvas 内除外。由于 CSS 背景样式不是 canvas 上下文的一部分,因此这将不起作用。有一些使用 html2canvas 库的解决方法,但是在 canvases.

上不支持圆锥渐变的浏览器中存在同样的问题。

浏览器starting to implement EyeDropperAPI但这仍然很少见,即使浏览器有它,它也是不稳定的API 并不真正适合生产使用。但是,如果支持,您可以像这样在常规 div 上将其与堆叠 CSS 背景渐变一起使用,不需要 canvas:

document.addEventListener('DOMContentLoaded', function() {
  document.getElementById('color-wheel').addEventListener('click', function() {
    (new EyeDropper()).open().then(function(result) {
      document.getElementById('color').innerText = result.sRGBHex;
    });
  });
});
#color-wheel {
  width: 150px;
  height: 150px;
  background: radial-gradient(white, transparent 80%),
              conic-gradient(#e43f00, #fae410, #55cc3b, #09adff, #6b0efd, #e70d86, #e43f00);
  border-radius: 50%;
}
<div id="color-wheel"></div>
Color: <span id="color"></span>

但正如我所说,这是不稳定的,在可预见的未来肯定不会在所有浏览器中工作。

免责声明:此答案旨在改进 rickdenhaan 的答案。

您可以堆叠 css 渐变以获得色轮,并使用简单的数学计算给定坐标处的颜色,而无需实际从色轮中选取颜色。 这种方法不使用 EyeDropper,因此它应该具有完整的浏览器支持。在下面的代码中,我将代码绑定到 mousemove 事件以便轻松测试它;只需将事件名称替换为“点击”即可获得预期的行为:

const colors = [
    {r: 0xe4, g: 0x3f, b: 0x00},
    {r: 0xfa, g: 0xe4, b: 0x10},
    {r: 0x55, g: 0xcc, b: 0x3b},
    {r: 0x09, g: 0xad, b: 0xff},
    {r: 0x6b, g: 0x0e, b: 0xfd},
    {r: 0xe7, g: 0x0d, b: 0x86},
    {r: 0xe4, g: 0x3f, b: 0x00}
];
document.addEventListener('DOMContentLoaded', function() {
    document.getElementById('color-wheel').addEventListener('mousemove', function(e) {
        var rect = e.target.getBoundingClientRect();
        //Compute cartesian coordinates as if the circle radius was 1
        var x = 2 * (e.clientX - rect.left) / (rect.right - rect.left) - 1;
        var y = 1 - 2 * (e.clientY - rect.top) / (rect.bottom - rect.top);
        //Compute the angle in degrees with 0 at the top and turning clockwise as expected by css conic gradient
        var a = ((Math.PI / 2 - Math.atan2(y, x)) / Math.PI * 180);
        if (a < 0) a += 360;
        //Map the angle between 0 and number of colors in the gradient minus one
        a = a / 360 * (colors.length - 1);  //minus one because the last item is at 360° which is the same as 0°
        //Compute the colors to interpolate
        var a0 = Math.floor(a) % colors.length;
        var a1 = (a0 + 1) % colors.length;
        var c0 = colors[a0];
        var c1 = colors[a1];
        //Compute the weights and interpolate colors
        var a1w = a - Math.floor(a);
        var a0w = 1 - a1w;
        var color = {
            r: c0.r * a0w + c1.r * a1w,
            g: c0.g * a0w + c1.g * a1w,
            b: c0.b * a0w + c1.b * a1w
        };
        //Compute the radius
        var r = Math.sqrt(x * x + y * y);
        if (r > 1) r = 1;
        //Compute the white weight, interpolate, and round to integer
        var cw = r < 0.8 ? (r / 0.8) : 1;
        var ww = 1 - cw;
        color.r = Math.round(color.r * cw + 255 * ww);
        color.g = Math.round(color.g * cw + 255 * ww);
        color.b = Math.round(color.b * cw + 255 * ww);
        //Compute the hex color code and apply it
        var xColor = rgbToHex(color.r, color.g, color.b);
        document.getElementById('color').innerText = xColor;
        document.getElementById('color').style.backgroundColor = xColor;
    });
});

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

function rgbToHex(r, g, b) {
    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
#color-wheel {
    width: 150px;
    height: 150px;
    background: radial-gradient(white, transparent 80%),
                conic-gradient(#e43f00, #fae410, #55cc3b, #09adff, #6b0efd, #e70d86, #e43f00);
    border-radius: 50%;
}
<div id="color-wheel"></div>
Color: <span id="color"></span>