RGB到真实的浅色Javascript转换

时间:2016-12-04 17:25:04

标签: javascript led light

我认为这是一个非常有趣的问题需要一个优雅的解决方案......

我有一个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>层叠在彼此的顶部而没有运气。有什么想法吗?

2 个答案:

答案 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。请注意,周围环境也会影响外观。

&#13;
&#13;
// 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;
&#13;
&#13;