我认为这是一个非常有趣的问题需要一个优雅的解决方案......
我有一个RGB值,例如205,50,63
。
我试图在网页上模拟RGB LED的颜色,就好像它是真实的生命之光。
例如,RGB颜色255,0,0
将在LED和网页上显示为红色。
同样,RGB颜色255,255,255
将在LED和网页上显示为白色。
但RGB颜色0,0,0
在LED上显示为关闭,并在网页上显示为黑色。
我想要实现的是0,0,0和255,255,255都显示为白色。就好像LED越暗,它就越白。
我一直试图将比例算法应用于值,然后将<div>
层叠在彼此的顶部而没有运气。有什么想法吗?
答案 0 :(得分:1)
我不确定你想象的是什么,但是读取你想要的输出,简单地放大以使最大值变为255会有什么问题?
function scaleUp(rgb) {
let max = Math.max(rgb.r, rgb.g, rgb.b);
if (!max) { // 0 or NaN
return {r: 255, g: 255, b: 255};
}
let factor = 255 / max;
return {
r: factor * rgb.r,
g: factor * rgb.g,
b: factor * rgb.b,
};
}
所以你会得到像
这样的结果scaleUp({r: 0, g: 0, b: 0}); // {r: 255, g: 255, b: 255}
scaleUp({r: 255, g: 0, b: 0}); // {r: 255, g: 0, b: 0}
scaleUp({r: 50, g: 80, b: 66}); // {r: 159.375, g: 255, b: 210.375}
请注意,这会将所有{x, 0, 0}
折叠为{255, 0, 0}
,这意味着{1, 0, 0}
与{1, 1, 1}
大不相同。如果这不可取,您需要考虑对此类案件的特殊处理
更多RGB提示;你可以获得更平滑的“更自然”的光线转换等等而不是x + y
,请sqrt(x*x + y*y)
这导致了如何解决问题的不同想法;添加白色和缩小
function scaleDown(rgb) {
let whiteAdded = {
r: Math.sqrt(255 * 255 + rgb.r * rgb.r),
g: Math.sqrt(255 * 255 + rgb.g * rgb.g),
b: Math.sqrt(255 * 255 + rgb.b * rgb.b)
};
return scaleUp(whiteAdded);
}
这一次
scaleDown({r: 0, g: 0, b: 0}); // {r: 255, g: 255, b: 255}
scaleDown({r: 255, g: 0, b: 0}); // {r: 255, g: 180.3122292025696, b: 180.3122292025696}
scaleDown({r: 50, g: 80, b: 66}); // {r: 247.94043129928136, g: 255, b: 251.32479296236951}
并且在边缘点附近跳跃较少,例如
scaleDown({r: 1, g: 0, b: 0}); // {r: 255, g: 254.99803923830171, b: 254.99803923830171}
最后,请注意这会将rgb映射到范围180..255
,因此如果您想保留“真正的红色”等,可以将其转换为0..255
function solution(rgb) {
let high = scaleDown(rgb);
return {
r: 3.4 * (high.r - 180),
g: 3.4 * (high.g - 180),
b: 3.4 * (high.b - 180),
};
}
所以
solution({r: 255, g: 0, b: 0}); // {r: 255, g: 1.0615792887366295, b: 1.0615792887366295}
solution({r: 1, g: 0, b: 0}); // {r: 255, g: 254.99333341022583, b: 254.99333341022583}
solution({r: 50, g: 80, b: 66}); // {r: 230.9974664175566, g: 255, b: 242.50429607205635}
答案 1 :(得分:0)
由于已经有答案,我不会详细介绍。
演示模拟多色清晰LED
使用复合操作"lighten"
通过重叠3个RGB图像来创建颜色。这是一个添加过程。还有一个白色通道可为整个LED增加白光。 RGB通道增加了额外的增益以使效果均匀,当蓝色为高时,红色被驱动下来。
当没有灯光时,只显示LED的图像。还有一个对比图像绘制,并在4色通道RGB&amp;之后。白。
使用一些更好的源图像(每个频道只使用一个,应该有2-3个),可以创建一个非常逼真的FX。请注意,周围环境也会影响外观。
// Load media (set of images for led)
var mediaReady = false;
var leds = new Image();
leds.src = "https://i.stack.imgur.com/tT1YV.png";
leds.onload = function () {
mediaReady = true;
}
var canLed = document.createElement("canvas");
canLed.width = 31;
canLed.height = 47;
var ctxLed = canLed.getContext("2d")
// display canvas
var canvas = document.createElement("canvas");
canvas.width = 31 * 20;
canvas.height = 47;
var ctx = canvas.getContext("2d");
var div = document.createElement("div");
div.style.background = "#999";
div.style.position = "absolute";
div.style.top = div.style.left = "0px";
div.style.width = div.style.height = "100%";
var div1 = document.createElement("div");
div1.style.fontFamily="Arial";
div1.style.fontSize = "28px";
div1.textContent ="Simple LED using layered RGB & white images.";
div.appendChild(div1);
div.appendChild(canvas);
document.body.appendChild(div);
const cPow = [1 / 7, 1 / 1, 1 / 3, 1 / 5]; // output gain for g,b,r,w (w is white)
var colourCurrent = {
r : 0,
g : 0,
b : 0,
w : 0
}
function easeInOut(x, pow) { // ease function
x = x < 0 ? 0 : x > 1 ? 1 : x;
xx = Math.pow(x, pow);
return xx / (xx + Math.pow(1 - x, pow));
}
var FX = { // composite operations
light : "lighter",
norm : "source-over",
tone : "screen",
block : "color-dodge",
hard : "hard-light",
}
function randB(min, max) { // random bell
if (max === undefined) {
max = min;
min = 0;
}
var r = (Math.random() + Math.random() + Math.random() + Math.random() + Math.random()) / 5;
return (max - min) * r + min;
}
function randL(min, max) { // linear
if (max === undefined) {
max = min;
min = 0;
}
var r = Math.random();
return (max - min) * r + min;
}
function drawSprite(index, alpha, fx) {
ctxLed.globalAlpha = alpha;
ctxLed.globalCompositeOperation = fx;
ctxLed.drawImage(leds, index * 32, 0, 31, 47, 0, 0, 31, 47);
}
var gbrw = [0, 0, 0, 0];
// Draws a LED using colours in col (sorry had images in wrong order so colour channels are green, blue, red and white
function drawLed(col) {
// get normalised values for each channel
gbrw[0] = col.g / 255;
gbrw[1] = col.b / 255;
gbrw[2] = col.r / 255;
gbrw[3] = col.w / 255;
gbrw[2] *= 1 - gbrw[1]; // suppress red if blue high
var total = (col.g / 255) * cPow[0] + (col.b / 255) * cPow[1] + (col.r / 255) * cPow[2] + (col.w / 255) * cPow[3];
total /= 8;
// display background
drawSprite(4, 1, FX.norm);
// show contrast by summing highlights
drawSprite(4, Math.pow(total, 4), FX.light);
// display each channel in turn
var i = 0;
while (i < 4) {
var v = gbrw[i]; // get channel normalised value
// add an ease curve and push intensity to full (over exposed)
v = easeInOut(Math.min(1, v), 2) * 4 * cPow[i]; // cPow is channel final gain
while (v > 0) { // add intensity for channel
drawSprite(i, easeInOut(Math.min(1, v), 4), FX.light);
if(i === 1){ // if blue add a little white
drawSprite(4, easeInOut(Math.min(1, v)/4, 4), FX.light);
}
v -= 1;
}
i++;
}
drawSprite(4, (1 - Math.pow(total, 4)) / 2, FX.block);
drawSprite(4, 0.06, FX.hard);
}
var gbrwT = [0, 0, 0, 0];
var move = 0.2;
ctx.fillRect(0, 0, canvas.width, canvas.height);
function update(time) {
if (mediaReady) {
time /= 1000;
var t = Math.sin(time / ((Math.sin(time / 5000) * 12300))) * 100;
var t = Math.sin(time / 12300) * 100;
var ttr = Math.sin(time / 12300 + t);
var ttg = Math.sin(time / 12400 + t * 10);
var ttb = Math.sin(time / 12500 + t * 15);
var ttw = Math.sin(time / 12600 + t * 20);
var tr = time / (2360 + t);
var tg = time / (2360 + t * 2);
var tb = time / (2360 + t * 3);
var tw = time / (2360 + t * 4);
for (var i = 0; i * 31 < canvas.width; i++) {
colourCurrent.r = Math.sin(tr) * 128 + 128;
colourCurrent.g = Math.sin(tg) * 128 + 128;
colourCurrent.b = Math.sin(tb) * 128 + 128;
colourCurrent.w = Math.sin(tw) * 128 + 128;
tr += ttr;
tg += ttg;
tb += ttb;
tw += ttw;
drawLed(colourCurrent);
ctx.drawImage(canLed, i * 31, 0);
}
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;