我在想办法在js中创建一个类似这样的色轮:。色轮应该有大约4096(*)个像素大小的元素,它们的颜色通过CSSbackground
规则设置。
我知道这不是创建颜色选择器的方式,通常情况下,任何东西都不应该有这么多的单像素DOM元素你不需要告诉我这些,也不需要想别的办法来完成。
我还想让每个像素大小的元素左对齐,而不是绝对定位。
(X):4096是所有速记十六进制代码(x x x)的数目,但是色轮没有单色值,除了白色。所以唯一颜色的实际数目是4081(?)
这是我想出来的代码(几乎什么都没有):
var p = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
for(var i = 0; i < p.length; i++)
{
for(var j = 0; j < p.length; j++)
{
for(var k = 0; k < p.length; k++)
{
document.write('<div style="background:#' + p[i] + p[j] + p[k] +'"></div>');
}
}
}
是我得到的结果(放大10倍),使用以下CSS:
div
{
float: left;
width: 10px;
height: 10px;
}
如你所见,这与我想要的相去甚远。因此,任何帮助都将非常感谢,因为我对如何实现这一点非常迷茫。我有颜色,但我不知道怎么把它们放在轮子里。
编辑:
除非有人碰巧给了我一个非常完整的解决方案,目前看来这比我的技术水平要高一点所以我愿意接受(我想)更容易实现的东西。
基本上,另一种可以接受的输出形式是这样的:
最佳答案
我之前发布了一个答案,它依赖于浏览器将颜色从hsl颜色空间转换为rgb颜色空间。不幸的是,这是一种方法,虽然简单,但没有产生所显示的图像。
为了正确地产生所需的输出,一个更简单的方法是使用HSV颜色模型-一个非常类似于HSL的模型。
当我们使用正确的颜色空间时,为任何给定的像素确定正确的颜色是一个简单的插值3个值-所有这些值都是线性变化的(变化量保持不变)。一端为0,另一端为1,表示中间某点为0.5)
首先,让我们看看您想要的输出,以及我们的hsv输入是如何相对于x和y坐标变化的。我们将从更易于可视化和创建的平面条开始。
扁钢
关于这张图片,我们可以观察到以下情况:
色调范围从左边缘的0到右边缘的360。
sat的范围从上边缘的0到1,在顶部之间的中间
&底部边缘。超过1的点,它被夹在1。
VAL的范围从顶部和底部之间的0到顶部和底部之间的1
底部。在点为0之前,它被钳制为0。
现在,让我们看看同一张图片的轮子表示。
色轮
如果你仔细看,你会发现,当包装成一个圆圈时,色带会产生色轮。车轮中心对应于带材的上边缘,外缘对应于下边缘。
这也是为什么我们可以显示,原来的车轮,你显示的是一个有点不准确的颜色空间表示,因为它有红色的左边缘基本上,你的图像是水平翻转的。;)
好的,这就显示了图像与HSV颜色空间的关系接下来,我们真的需要能够在飞行中创造它们。这是相当直接的,现在我们有计划如何去做。
完成后,我们将得到两张画布-这些是我用于注释的图像从那以后,你可以有几种方法来解决这个问题。
您可以:允许用户选择任何他们喜欢的颜色,然后再从短手十六进制值集中返回最接近的颜色。
或者你可以:备份一点,只在画布上设置那些在同一组短手值中的颜色。
一个需要更长的时间来计算所选的颜色,而另一个则需要更长的时间来计算初始图像。
我把实现的那一部分留给了您,而不是选择:避开这么多DOM元素的想法,只使用2个画布,而且,根据我链接到@MDN的代码,简单地按照选择的颜色选择。
function newEl(tag){return document.createElement(tag)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
var strip = makeCanvas();
strip.addEventListener('mousemove', pick);
document.body.appendChild( strip );
var wheel = makeWheel(256);
wheel.addEventListener('mousemove', pick);
document.body.appendChild( wheel );
}
var hsv2rgb = function(hsv) {
var h = hsv.hue, s = hsv.sat, v = hsv.val;
var rgb, i, data = [];
if (s === 0) {
rgb = [v,v,v];
} else {
h = h / 60;
i = Math.floor(h);
data = [v*(1-s), v*(1-s*(h-i)), v*(1-s*(1-(h-i)))];
switch(i) {
case 0:
rgb = [v, data[2], data[0]];
break;
case 1:
rgb = [data[1], v, data[0]];
break;
case 2:
rgb = [data[0], v, data[2]];
break;
case 3:
rgb = [data[0], data[1], v];
break;
case 4:
rgb = [data[2], data[0], v];
break;
default:
rgb = [v, data[0], data[1]];
break;
}
}
return rgb;
};
function clamp(min, max, val)
{
if (val < min) return min;
if (val > max) return max;
return val;
}
function makeCanvas()
{
var can, ctx;
can = newEl('canvas');
ctx = can.getContext('2d');
can.width = 360;
can.height = 100;
var span = newEl('span');
var imgData = ctx.getImageData(0,0,360,100);
var xPos, yPos, index;
var height=imgData.height, width=imgData.width;
for (yPos=0; yPos<height; yPos++)
{
for (xPos=0; xPos<width; xPos++)
{
// this is the point at which the S & V values reach
// the peaks or start to change. 2 means height/2
// so a divisor of 3 would mean the 'break-points'
// were at the 1/3 and 2/3 positions
// while a divisor of 4 would imply 1/4 and 3/4
//
// Have a look at the generated images using the eye-
// dropper tool of an image program (Gimp, Photoshop,
// etc) that allows you to choose the HSV colour
// model, to get a better idea of what I'm saying
// here.
var divisor = 2;
var hue = xPos;
var sat = clamp(0, 1, yPos / (height/divisor) );
var val = clamp(0, 1, (height-yPos) / (height/divisor) );
var rgb = hsv2rgb( {hue:hue, sat:sat, val:val} );
index = 4 * (xPos + yPos*360);
imgData.data[ index + 0 ] = rgb[0] * 255; // r
imgData.data[ index + 1 ] = rgb[1] * 255; // g
imgData.data[ index + 2 ] = rgb[2] * 255; // b
imgData.data[ index + 3 ] = 255; // a
}
}
ctx.putImageData(imgData, 0, 0);
return can;
}
// see the comment in the above function about the divisor. I've
// hard-coded it here, to 2
// diameter/2 corresponds to the max-height of a strip image
function makeWheel(diameter)
{
var can = newEl('canvas');
var ctx = can.getContext('2d');
can.width = diameter;
can.height = diameter;
var imgData = ctx.getImageData(0,0,diameter,diameter);
var maxRange = diameter / 2;
for (var y=0; y<diameter; y++)
{
for (var x=0; x<diameter; x++)
{
var xPos = x - (diameter/2);
var yPos = (diameter-y) - (diameter/2);
var polar = pos2polar( {x:xPos, y:yPos} );
var sat = clamp(0,1,polar.len / ((maxRange/2)));
var val = clamp(0,1, (maxRange-polar.len) / (maxRange/2) );
var rgb = hsv2rgb( {hue:polar.ang, sat:sat, val:val} );
var index = 4 * (x + y*diameter);
imgData.data[index + 0] = rgb[0]*255;
imgData.data[index + 1] = rgb[1]*255;
imgData.data[index + 2] = rgb[2]*255;
imgData.data[index + 3] = 255;
}
}
ctx.putImageData(imgData, 0,0);
return can;
}
function deg2rad(deg)
{
return (deg / 360) * ( 2 * Math.PI );
}
function rad2deg(rad)
{
return (rad / (Math.PI * 2)) * 360;
}
function pos2polar(inPos)
{
var vecLen = Math.sqrt( inPos.x*inPos.x + inPos.y*inPos.y );
var something = Math.atan2(inPos.y,inPos.x);
while (something < 0)
something += 2*Math.PI;
return { ang: rad2deg(something), len: vecLen };
}
function pick(event)
{
var can = this;
var ctx = can.getContext('2d');
var color = document.getElementById('color');
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
var rgba = 'rgba(' + data[0] + ',' + data[1] +
',' + data[2] + ',' + (data[3] / 255) + ')';
color.style.background = rgba;
color.textContent = rgba;
}
canvas
{
border: solid 1px red;
}
<div id="color" style="width: 200px; height: 50px; float: left;"></div>