nic*_*ick 15 javascript interpolation gradient colors hsv
我正在尝试在HSV颜色空间中插入两种颜色之间以产生平滑的颜色渐变.
我正在使用线性插值,例如:
h = (1 - p) * h1 + p * h2
s = (1 - p) * s1 + p * s2
v = (1 - p) * v1 + p * v2
Run Code Online (Sandbox Code Playgroud)
(其中p是百分比,h1,h2,s1,s2,v1,v2是两种颜色的色调,饱和度和值分量)
这对s和v产生了良好的结果,但对于h则没有.由于色调分量是一个角度,计算需要计算出h1和h2之间的最短距离,然后沿正确的方向(顺时针或逆时针)进行插值.
我应该使用什么公式或算法?
编辑:通过遵循杰克的建议我修改了我的JavaScript渐变功能,它运作良好.对于任何有兴趣的人,这是我最终得到的:
// create gradient from yellow to red to black with 100 steps
var gradient = hsbGradient(100, [{h:0.14, s:0.5, b:1}, {h:0, s:1, b:1}, {h:0, s:1, b:0}]);
function hsbGradient(steps, colours) {
var parts = colours.length - 1;
var gradient = new Array(steps);
var gradientIndex = 0;
var partSteps = Math.floor(steps / parts);
var remainder = steps - (partSteps * parts);
for (var col = 0; col < parts; col++) {
// get colours
var c1 = colours[col],
c2 = colours[col + 1];
// determine clockwise and counter-clockwise distance between hues
var distCCW = (c1.h >= c2.h) ? c1.h - c2.h : 1 + c1.h - c2.h;
distCW = (c1.h >= c2.h) ? 1 + c2.h - c1.h : c2.h - c1.h;
// ensure we get the right number of steps by adding remainder to final part
if (col == parts - 1) partSteps += remainder;
// make gradient for this part
for (var step = 0; step < partSteps; step ++) {
var p = step / partSteps;
// interpolate h, s, b
var h = (distCW <= distCCW) ? c1.h + (distCW * p) : c1.h - (distCCW * p);
if (h < 0) h = 1 + h;
if (h > 1) h = h - 1;
var s = (1 - p) * c1.s + p * c2.s;
var b = (1 - p) * c1.b + p * c2.b;
// add to gradient array
gradient[gradientIndex] = {h:h, s:s, b:b};
gradientIndex ++;
}
}
return gradient;
}
Run Code Online (Sandbox Code Playgroud)
Jac*_*ack 11
您应该只需要找出从开始色调到结束色调的最短路径.这可以很容易地完成,因为色调值的范围是0到255.
您可以先从较高的色调中减去较低的色调,然后在较低的色调中加256,再次检查交换操作数的差异.
int maxCCW = higherHue - lowerHue;
int maxCW = (lowerHue+256) - higherHue;
Run Code Online (Sandbox Code Playgroud)
因此,您将获得两个值,较大的一个决定您应该顺时针还是逆时针.然后你必须找到一种方法使插值在模数256的色调上运行,所以如果你是插值246,20如果系数是>= 0.5f你应该将色调重置为0(因为它达到256,hue = hue%256无论如何).
实际上,如果你在0上插值时不关心色调,但只是在计算新色调后应用模数运算符它应该可以工作.
虽然这个答案很晚,但公认的答案是不正确的,即色调应该在 [0, 255] 之内;也可以通过更清晰的解释和代码来做更多的正义。
Hue是区间[0, 360)内的角值;一个完整的圆圈,其中 0 = 360。HSV 颜色空间比 RGB 更容易可视化,对人类更直观。HSV 形成一个圆柱体,其中的切片在许多颜色选择器中显示,而 RGB 实际上是一个立方体,对于颜色选择器来说并不是一个很好的选择;大多数使用它的人必须使用比 HSV 选择器所需的更多的滑块。
插入色调时的要求是选择较小的弧线以从一种色调到达另一种色调。所以给定两个色调值,有四种可能性,下面给出了示例角度:
? | ? 180 | > 180
--|---------|---------
+ | 40, 60 | 310, 10
? | 60, 40 | 10, 310
if ? = 180 then both +/? rotation are valid options
Run Code Online (Sandbox Code Playgroud)
让我们以+逆时针和?顺时针旋转为例。如果绝对值的差异超过 180,则将其归一化为 ± 360,以确保幅度在 180 以内;这也正确地扭转了方向。
var d = h2 - h1;
var delta = d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0);
Run Code Online (Sandbox Code Playgroud)
现在只需除以delta所需的步数即可获得每个循环迭代的权重,以在插值期间添加到起始角度。
var new_angle = start + (i * delta);
Run Code Online (Sandbox Code Playgroud)
相关功能摘自完整代码如下:
function interpolate(h1, h2, steps) {
var d = h2 - h1;
var delta = (d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0)) / (steps + 1.0);
var turns = [];
for (var i = 1; d && i <= steps; ++i)
turns.push(((h1 + (delta * i)) + 360) % 360);
return turns;
}
Run Code Online (Sandbox Code Playgroud)
? | ? 180 | > 180
--|---------|---------
+ | 40, 60 | 310, 10
? | 60, 40 | 10, 310
if ? = 180 then both +/? rotation are valid options
Run Code Online (Sandbox Code Playgroud)
var d = h2 - h1;
var delta = d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0);
Run Code Online (Sandbox Code Playgroud)