javascript - 使用 JavaScript 创建色轮

标签 javascript html css image algorithm

我正在尝试找出一种方法来创建与此类似的色轮:enter image description here , 在 JS 中。色轮应该有 ~4096(*) 个元素,大小为单个像素,它们的颜色通过 CSS background 规则设置。

我知道这不是您创建颜色选择器的方式,而且通常情况下您不应该为任何东西使用如此多的单像素 DOM 元素。 您无需将此告诉我或尝试找出其他方法来完成此操作。

我也有兴趣让每个像素大小的元素左对齐,而不是绝对定位。

(x):4096 是所有简写十六进制代码 (#XXX) 的编号,但色轮没有单色值,白色除外。所以唯一颜色的实际数量是 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>');
        }
    }
}

enter image description here是我得到的结果(放大 10 倍),使用以下 CSS:

div
{
    float: left;
    width: 10px;
    height: 10px;
}

如您所见,这与我想要的相去甚远。因此,我们将不胜感激任何帮助,因为我对如何实现这一目标一无所知。我有颜色,但我不知道如何将它们排列在轮子中。


编辑:
除非有人碰巧给我一个非常完整的解决方案,否则目前这似乎有点超出我的技能水平。所以我愿意接受一些(我认为)更容易实现的东西。

基本上另一种可接受的输出形式是这样的:enter image description here

最佳答案

我之前发布了一个答案,该答案依赖于浏览器将颜色从 HSL 颜色空间转换为 RGB 颜色。不幸的是,这种方法虽然简单,但无法生成所示图像。

为了正确生成所需的输出,一种更简单的方法是改用 HSV 颜色模型 - 一种与 HSL 模型非常相似的方法。

当我们使用正确的颜色空间时,确定任何给定像素的正确颜色是一件简单的事情,只需插值 3 个值 - 所有这些值都线性变化(变化量保持不变。0 在一端,1 在另一端另一端将意味着在它们之间的中间点为 0.5)

首先,让我们看看您想要的输出以及我们的 HSV 输入如何随 X 和 Y 坐标变化。我们将从更容易可视化和创建的扁平 strip 开始。

平条

enter image description here

我们可以观察到这张图片的以下内容:

  1. 色调范围从左边缘的 0 到右边缘的 360。
  2. 饱和度范围从顶部边缘的 0 到顶部中间的 1 & 底部边缘。超出它达到 1 的点,它被限制在 1。
  3. val 的范围从顶部和底部中间的 0 到 1 底端。在它为 0 的点之前,它被限制为 0。

现在,让我们看一下同一张图片的轮子表示。

色轮

enter image description here 如果你仔细观察,你会发现当 strip 缠绕成一个圆圈时会产生色轮。轮子的中心对应于 strip 的顶边,外边对应于底边。

这也是我们如何证明您展示的原始轮子对色彩空间的表示有些不准确,因为它的左边缘有红色。基本上,您的图像已水平翻转。 ;)

好的,这说明了图像与 HSV 色彩空间的关系。接下来,我们真的需要能够即时创建它们。现在我们已经制定了如何进行的计划,这非常简单。

完成此操作后,我们将得到 2 幅 Canvas - 这些是我用于注释的图像。从那里,有几种方法可以解决。 您可以: 允许用户选择他们喜欢的任何颜色,然后从一组简写的十六进制值中返回最接近的颜色给他们。 或者您可以:稍作备份,仅将 Canvas 上的颜色设置为同一组简写值中的颜色。

一个计算所选颜色的时间会更长,而另一个计算初始图像的时间会更长。

我已将这部分实现留给您,而不是选择:避开这么多 DOM 元素的想法,只使用 2 个 Canvas ,并且根据我的代码简单地选择与所选完全相同的颜色链接到@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 - 使用 JavaScript 创建色轮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39386272/

相关文章:

html - 在表angular2的悬停列上显示文本和隐藏图像

html - ng-bootstrap TimePicker 微调器轮廓可见

html - 制作网格布局时出现白线

html - z-index - 标志在标题后面

css - 在 ionic 3 上组织网格

javascript - OnScroll 返回时粘性标题

javascript - jQuery AJAX 循环刷新 jQueryUI ProgressBar

javascript - 对于一系列图片,我希望每个图像都有不同的音频

javascript - 使用 AJAX/Javascript 创建 Rails 对象

jquery - 当摘录超过一定限制时添加底部填充