如何创建像 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:答案是空的
执行 UPNP 扫描不会返回 Philips Hue Bridge