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

Posted

技术标签:

【中文标题】如何创建像 Philips Hue 这样的颜色选择器圈【英文标题】:How to create a color picker circle like Phillips Hue 【发布时间】:2021-12-25 16:08:04 【问题描述】:

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

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

我已经搜索了互联网,但没有找到任何与我的问题有关的内容。

P.S.:也许 css border-radius 会帮助做一个圆圈。

【问题讨论】:

试试这个:medium.com/@bantic/… 【参考方案1】:

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

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

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

可以使用堆叠的 CSS 背景渐变,但浏览器目前没有可靠的方法来确定特定 X/Y 坐标处的单个像素的背景颜色,除了在画布内。并且由于 CSS 背景样式不是画布上下文的一部分,因此它不起作用。有一些使用 html2canvas 库的解决方法,但这在不支持画布上的圆锥渐变的浏览器中存在同样的问题。

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

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>

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

【讨论】:

【参考方案2】:

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

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

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>

【讨论】:

以上是关于如何创建像 Philips Hue 这样的颜色选择器圈的主要内容,如果未能解决你的问题,请参考以下文章

Philips Hue REST api/nupnp:答案是空的

PHILIPS HUE APPLE SDK 的问题

将 Philips Hue 与端口转发结合使用

执行 UPNP 扫描不会返回 Philips Hue Bridge

如何使用mfc添加像ms paint这样的颜色选择器[重复]

像 Hue 的 .hiverc