使用JavaScript创建色轮 [英] Creating a colorwheel with javascript
问题描述
我正在尝试创建一种类似于以下内容的色轮的方法:.色轮应具有〜4096 (*)元素,其大小为单个像素,并通过CSS background
规则设置其颜色.
I'm trying to figure out a way to create a colorwheel similar to this:, in JS. The colorwheel should have ~4096(*) elements the size of a single pixel, with their color set via a CSS background
rule.
我知道这不是您应该创建的颜色选择器的方式,并且通常您永远都不会拥有那么多的单像素DOM元素. 您不需要告诉我这个信息或尝试找出其他方法来完成此操作.
我还想使每个像素大小的元素左对齐,而不是例如绝对定位.
I'd also be interested in having each of the pixel-sized elements be left-aligned, instead of for example absolutely-positioned.
(x):4096是所有速记十六进制代码(#XXX)的数量,但色轮除白色外没有单色值.因此,实际的唯一颜色数将是4081(?)
(x): 4096 is the number of all shorthand HEX codes (#XXX), but the colorwheel doesn't have monochrome values, except for white. So the actual number of unique colors would be 4081(?)
这是我设法提供的代码(几乎没有):
This is the code I've managed to come up with (pretty much nothing):
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>');
}
}
}
和是使用以下CSS生成的结果(放大10倍):
And is the result I get (zoomed 10x), with the following CSS:
div
{
float: left;
width: 10px;
height: 10px;
}
如您所见,它与我想要的相差很远.因此,任何帮助将不胜感激,因为我对如何完成此任务一无所知.我有颜色,但是我不知道如何将它们排列在轮子上.
As you can see, it's pretty far from what I want. So any help would be greatly appreciated, since I'm quite lost on how to accomplish this. I've got the colors, but I don't know how to arrange them in a wheel.
除非有人碰巧为我提供了一个非常完整的解决方案,否则目前看来这似乎有点超出我的技能水平.因此,我愿意为某些(我认为)易于实现的事情感到满意.
Unless someone happens to give me a pretty much complete solution to this, it seems that this is a bit above my skill-level, at the moment. So I'm willing to settle for something that (I assume) would be easier to implement.
基本上可以接受的另一种形式的输出将是这样的:
Basically another form of output that would be acceptable, would be something like this:
推荐答案
我以前发布了一个答案,该答案依赖于浏览器将颜色从HSL颜色空间转换为RGB颜色空间.不幸的是,这种方法虽然简单,却无法产生所显示的图像.
I had previously posted an answer that relied upon the browser converting a colour from the HSL colour-space to the RGB one. Unfortunately, this was an approach that while simple, didn't produce the images shown.
为了正确产生所需的输出,一种更简单的方法是改为使用HSV颜色模型-一种与HSL非常相似的颜色模型.
In order to correctly produce the desired outputs, a far easier method is to instead utilise the HSV colour model - one which is quite similar to the HSL one.
当我们使用正确的色彩空间时,为任意给定像素确定正确的色彩只需插入3个值即可,所有这些值都是线性变化的(变化量保持恒定.一端为0,一端为1.另一端表示在它们中间的0.5点处
When we use the correct colour-space, determining the correct colour for any given pixel is a simple matter of interpolating 3 values - all of which change linearly (the amount of change remains constant. 0 at one end, 1 at the other end will mean 0.5 at a point half-way between them)
首先,让我们看一下所需的输出以及我们的HSV输入相对于X和Y坐标的变化.我们将从更容易可视化和创建的扁平条开始.
First, lets look at your desired outputs and how our HSV inputs change with respect to X and Y coordinates. We'll start of with the easier to visualise and create - the flat strip.
平条
我们可以观察到有关此图像的以下内容:
We can observe the following about this image:
- 色相的范围从左侧的0到右侧的360.
- sat范围从顶部边缘的0到顶部之间的一半的1, &底部边缘.超过该点达到1时,将其固定为1.
- 值的范围从顶部和底部之间的0到1处的1 底端.在该点为0之前,将其钳位为0.
- The hue ranges from 0 at the left edge to 360 at the right edge.
- The sat ranges from 0 at the top edge to 1, half-way between the top & bottom edges. Beyond the point it reaches 1, it's clamped to 1.
- The val ranges from 0 half-way between the top and bottom to 1 at the bottom. Before the point it is 0, it's clamped to 0.
现在,让我们看一下同一张图片的车轮表示.
Now, let's look at the wheel representation of the same picture.
色轮
如果仔细观察,您会发现该条带缠绕成一个圆圈后会产生色轮.轮的中心对应于带的上边缘,外边缘对应于底边缘.
If you look closely, you'll see that the strip, when wrapped into a circle will produce the colour-wheel. The centre of the wheel corresponds to the top-edge of the strip and the outer edge corresponds to the bottom edge.
这也是我们可以证明所显示的原始转轮在色彩空间上表现得不太准确的一种方式,因为它的左边缘为红色.基本上,您的图像已被水平翻转. ;)
This is also how we can show that the original wheel you showed is a somewhat innacurate representation of the colour-space, since it has the red on the left edge. Basically, your image has been flipped horizontally. ;)
好的,然后显示图像如何与HSV色彩空间相关.接下来,我们确实需要能够即时创建它们.现在,我们已经制定了解决该问题的计划.
Okay, that then shows how the images are related to the HSV colour-space. Next, we really need to be able to create them on the fly. This is fairly straight-forward now we've the plan for how to go about it.
完成后,我们将得到2张画布-这些是我用于注释的图像.从那里开始,有两种方法可以解决. 您可以:允许用户选择他们喜欢的任何颜色,然后再将其从速记十六进制值集中返回给他们最接近的颜色. 或者,您可以:备份一点,仅将画布上的颜色设置为相同的一组速记值中的颜色.
Once this is done, we'll end up with 2 canvases - these were the images I used for the annotations. From there, there's a couple of ways you could go about it. You could: allow the user to pick any colour they like, before returning to them the closest colour from the set of short-hand hex values. Or you could: back-up a little, only setting colours on the canvas to those which are in the same set of short-hand values.
一个将花费更长的时间来计算所选的颜色,而另一个将花费更长的时间来计算初始图像.
One will take longer to calculate the chosen colour, while the other will take longer to calculate the initial images.
我将实现的那部分留给您,而是选择:避免使用许多DOM元素的想法,而是仅使用2个canvas,并且根据代码我完全按照选择的颜色进行选择.链接到@ MDN.
I have left that part of the implementation up to you, instead opting to: eschew the idea of so many DOM elements, using just 2 canvas instead and also, to simply pick the colour exactly as chosen, based off the code I linked to @ 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>
这篇关于使用JavaScript创建色轮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!