javascript 连续颜色过渡

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19639506/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 16:18:07  来源:igfitidea点击:

Continuous Color Transition

javascripthtmlcss

提问by akinuri

I'm trying to create a continuous color transition like the one in Windows 8 installation, We're getting your PC ready.

我正在尝试创建一种像 Windows 8 安装中那样的连续颜色过渡,我们正在为您的 PC 做好准备

I couldn't figure out how to write the shiftfunction. Checking all R, G, B values and matching the current color to the next color.

我无法弄清楚如何编写该shift函数。检查所有 R、G、B 值并将当前颜色与下一种颜色匹配。

Can anyone help me on this? Or let me know if there's a better approach to it than this?

谁可以帮我这个事?或者让我知道是否有比这更好的方法?

function switchColor(id) {
    var elm = document.getElementById(id);

    //  getting elm's current rgb values
    var elmColor = window.getComputedStyle(elm).getPropertyValue("background");
    var startIndex = elmColor.indexOf("(");
    var finishIndex = elmColor.indexOf(")");
    var elmRGB = elmColor.substring(startIndex + 1, finishIndex);
    var currentColor = elmRGB.split(",");
    for (var i = 0; i<3; i++) { currentColor[i] = currentColor[i].trim(); }

    //  generating a random color => [r, g, ,b]
    var nextColor = [];
    for (var i = 0; i < 3; i++) {
        nextColor[i] = Math.floor(Math.random()*250);
    }

    //  function to convert rgb array to hex color => [r, g, b] = #rgb
    function rgbToHex(clr) {
        var rgb = clr;
        var hex;
        var hex1 = rgb[0].toString(16);
        var hex2 = rgb[1].toString(16);
        var hex3 = rgb[2].toString(16);
        if (hex1.length < 2) { hex1 = "0" + hex1; }
        if (hex2.length < 2) { hex2 = "0" + hex2; }
        if (hex3.length < 2) { hex3 = "0" + hex3; }
        return hex = "#" + hex1 + hex2 + hex3;
    }

    //  checking if nextColor rgb values are greater than current rgb's
    //  so we can increase or decrease for smooth transition
    var status = [];
    for (var i = 0; i < 3; i++) {
        if (nextColor[i] > currentColor[i]) { status.push(1); }
        else { status.push(0); }
    }

    //    this isn't part of the code, just testing
    elm.style.background = rgbToHex(nextColor);

    function shift() {
        //  shift between colors
        //  modify currentColor's rgb values and apply it to the elm
        //  elm.style.background = rgbToHex(currentColor);
    }
    var handler = setInterval(shift, 100);
}
setInterval(function() { switchColor("sandbox"); }, 2000);

JSFiddle

JSFiddle

回答by akinuri

Check this JSFiddlefor a transition with a fancy graph.

检查此JSFiddle以获取带有精美图形的过渡。

/* ==================== Required Functions ==================== */
// This is required to get the initial background-color of an element.
// The element might have it's bg-color already set before the transition.
// Transition should continue/start from this color.
// This will be used only once.
function getElementBG(elm) {
 var bg = getComputedStyle(elm).backgroundColor;
  bg = bg.match(/\((.*)\)/)[1];
  bg = bg.split(",");
 for (var i = 0; i < bg.length; i++) {
  bg[i] = parseInt(bg[i], 10);
 }
 if (bg.length > 3) { bg.pop(); }
 return bg;
}

// A function to generate random numbers.
// Will be needed to generate random RGB value between 0-255.
function random() {
 if (arguments.length > 2) {
  return 0;
 }
 switch (arguments.length) {
  case 0:
   return Math.random();
  case 1:
   return Math.round(Math.random() * arguments[0]);
  case 2:
   var min = arguments[0];
   var max = arguments[1];
   return Math.round(Math.random() * (max - min) + min);
 }
}

// Generates a random RGB value.
function generateRGB(min, max) {
 var min  = min || 0;
 var max  = min || 255;
 var color = [];
 for (var i = 0; i < 3; i++) {
  var num = random(min, max);
  color.push(num);
 }
 return color;
}

// Calculates the distance between the RGB values.
// We need to know the distance between two colors
// so that we can calculate the increment values for R, G, and B.
function calculateDistance(colorArray1, colorArray2) {
 var distance = [];
 for (var i = 0; i < colorArray1.length; i++) {
  distance.push(Math.abs(colorArray1[i] - colorArray2[i]));
 }
 return distance;
}

// Calculates the increment values for R, G, and B using distance, fps, and duration.
// This calculation can be made in many different ways.
function calculateIncrement(distanceArray, fps, duration) {
 var fps   = fps || 30;
 var duration = duration || 1;
 var increment = [];
 for (var i = 0; i < distanceArray.length; i++) {
  var incr = Math.abs(Math.floor(distanceArray[i] / (fps * duration)));
  if (incr == 0) {
   incr = 1;
  }
  increment.push(incr);
 }
 return increment;
}

// Converts RGB array [32,64,128] to HEX string #204080
// It's easier to apply HEX color than RGB color.
function rgb2hex(colorArray) {
 var color = [];
 for (var i = 0; i < colorArray.length; i++) {
  var hex = colorArray[i].toString(16);
  if (hex.length < 2) { hex = "0" + hex; }
  color.push(hex);
 }
 return "#" + color.join("");
}

/* ==================== Setup ==================== */
// Duration is not what it says. It's a multiplier in the calculateIncrement() function.
// duration = 1-4, fast-to-slow
var fps    = 30;
var duration  = 3;
var transElement = document.body;
var currentColor = getElementBG(transElement);
var transHandler = null;

startTransition();

/* ==================== Transition Initiator ==================== */
function startTransition() {
 clearInterval(transHandler);
 
 targetColor = generateRGB();
 distance = calculateDistance(currentColor, targetColor);
 increment = calculateIncrement(distance, fps, duration);
 
 transHandler = setInterval(function() {
  transition();
 }, 1000/fps);
}

/* ==================== Transition Calculator ==================== */
function transition() {
 // checking R
 if (currentColor[0] > targetColor[0]) {
  currentColor[0] -= increment[0];
  if (currentColor[0] <= targetColor[0]) {
   increment[0] = 0;
  }
 } else {
  currentColor[0] += increment[0];
  if (currentColor[0] >= targetColor[0]) {
   increment[0] = 0;
  }
 }
 
 // checking G
 if (currentColor[1] > targetColor[1]) {
  currentColor[1] -= increment[1];
  if (currentColor[1] <= targetColor[1]) {
   increment[1] = 0;
  }
 } else {
  currentColor[1] += increment[1];
  if (currentColor[1] >= targetColor[1]) {
   increment[1] = 0;
  }
 }
 
 // checking B
 if (currentColor[2] > targetColor[2]) {
  currentColor[2] -= increment[2];
  if (currentColor[2] <= targetColor[2]) {
   increment[2] = 0;
  }
 } else {
  currentColor[2] += increment[2];
  if (currentColor[2] >= targetColor[2]) {
   increment[2] = 0;
  }
 }
 
 // applying the new modified color
 transElement.style.backgroundColor = rgb2hex(currentColor);
 
 // transition ended. start a new one
 if (increment[0] == 0 && increment[1] == 0 && increment[2] == 0) {
  startTransition();
 }
}
body {
  background: white;
}

回答by Vlad Bezden

Here is another implementation of color transition/animation using pure JavaScript and CSS

这是使用纯 JavaScript 和 CSS 的另一种颜色过渡/动画实现

const randomColor = () => '#' + Math.random().toString(16).substr(-6)
const changeColor = () => document.body.style.backgroundColor = randomColor()

setInterval(() => {
  changeColor()
}, 5000)

// start color animation as soon as document is ready
document.onreadystatechange = () => {
  if (document.readyState === 'complete') {
    changeColor()
  }
}
body {
  transition: background 5s;
}

回答by Igor Benikov

where is too many variables? what about this way?

哪里有太多变数?这种方式怎么样?

var el = document.getElementById("sandbox"),
    interval = 2000;

function getNewColor(){
    //generate color
}

function getOldColor(el){
    //get current color
}

function switchColor(el, oldColor, newColor){
    //change color
}

setInterval(function(){

    swithColors(el, getOldColor(el), getNewColor());

},interval);

回答by ffdigital

Thanks to akinuri I managed to adapt his answer to a dynamic function that runs on requestanimationframe. Thanks again akinuri, nice code. ps: currentColor and targetColor request a string with the rgb value('rgb(0,0,0)')

感谢 akinuri,我设法将他的答案调整为在 requestanimationframe 上运行的动态函数。再次感谢akinuri,很好的代码。ps: currentColor 和 targetColor 请求一个带有 rgb 值的字符串('rgb(0,0,0)')

function startColorFade(fps, duration, element, currentColor, targetColor) {
    var stop = false;
    var fpsInterval = 1000 / fps;
    var now;
    var then = Date.now();
    var elapsed;
    var startTime = then;
    var currentColorArray = getElementBG(currentColor);
    var targetColorArray  = getElementBG(targetColor);
    var distance = calculateDistance(currentColorArray, targetColorArray);
    var increment = calculateIncrement(distance, fps, duration);
    animateColor(duration, element, currentColorArray, targetColorArray, increment, stop, fpsInterval, now, then, elapsed, startTime);
}
function animateColor( duration, element, currentColorArray, targetColorArray, increment, stop, fpsInterval, now, then, elapsed, startTime ) {  
    var step = function() {
        if (stop) {
            return;
        }       
        // request another frame
        requestAnimationFrame(function() //arguments can passed on the callback by an anonymous funtion 
        {
            animateColor(duration, element, currentColorArray, targetColorArray, increment, stop, fpsInterval, now, then, elapsed, startTime);
            colorTransition(element, currentColorArray, targetColorArray, increment);
        });     
        // calc elapsed time since last loop
        now = Date.now();
        elapsed = now - then;       
        // if enough time has elapsed, draw the next frame
        if (elapsed > fpsInterval) {
            // Get ready for next frame by setting then=now, but...
            // Also, adjust for fpsInterval not being multiple of 16.67
            then = now - (elapsed % fpsInterval);
            // draw stuff here    
            var sinceStart = now - startTime;               
        }   
        if (sinceStart / 1000 * 100 >= duration * 100)
        {
            stop = true;
        }   
    }
    step();
}
function colorTransition(element, currentColorArray, targetColorArray, increment) {

    // checking R
    if (currentColorArray[0] > targetColorArray[0]) {
        currentColorArray[0] -= increment[0];
        if (currentColorArray[0] <= targetColorArray[0]) {
            increment[0] = 0;
        }
    } else {
        currentColorArray[0] += increment[0];
        if (currentColorArray[0] >= targetColorArray[0]) {
            increment[0] = 0;
        }
    }    
    // checking G
    if (currentColorArray[1] > targetColorArray[1]) {
        currentColorArray[1] -= increment[1];
        if (currentColorArray[1] <= targetColorArray[1]) {
            increment[1] = 0;
        }
    } else {
        currentColorArray[1] += increment[1];
        if (currentColorArray[1] >= targetColorArray[1]) {
            increment[1] = 0;
        }
    }    
    // checking B
    if (currentColorArray[2] > targetColorArray[2]) {
        currentColorArray[2] -= increment[2];
        if (currentColorArray[2] <= targetColorArray[2]) {
            increment[2] = 0;
        }
    } else {
        currentColorArray[2] += increment[2];
        if (currentColorArray[2] >= targetColorArray[2]) {
            increment[2] = 0;
        }
    }    
    // apply the new modified color
    element.style.backgroundColor = rgb2hex(currentColorArray);    

}
function getElementBG(elmBGColor) { 
    var bg  = elmBGColor; // i.e: RGB(255, 0, 0)
        bg  = bg.match(/\((.*)\)/)[1];
        bg  = bg.split(",");
    for (var i = 0; i < bg.length; i++) {
        bg[i] = parseInt(bg[i], 10);
    }
    if (bg.length > 3) { bg.pop(); }
    return bg; // return array
}
function calculateDistance(colorArray1, colorArray2) {
    var distance = [];
    for (var i = 0; i < colorArray1.length; i++) {
        distance.push(Math.abs(colorArray1[i] - colorArray2[i]));
    }
    return distance;
}
function calculateIncrement(distanceArray, fps, duration) {
    var increment = [];
    for (var i = 0; i < distanceArray.length; i++) {
        increment.push(Math.abs(Math.floor(distanceArray[i] / (fps * duration))));
        if (increment[i] == 0) {
            increment[i]++;
        }
    }
    return increment;
}
function rgb2hex(colorArray) {
    var hex = [];
    for (var i = 0; i < colorArray.length; i++) {
        hex.push(colorArray[i].toString(16));
        if (hex[i].length < 2) { hex[i] = "0" + hex[i]; }
    }
    return "#" + hex.join("");
}
 //random rgb values in array, very nice
function generateRGB(min, max) {
    var min   = min || 0;
    var max   = max || 255;
    var color = [];
    for (var i = 0; i < 3; i++) {
        var num = Math.floor(Math.random() * max);
        while (num < min) {
            num = Math.floor(Math.random() * max);
        }
        color.push(num);
    }
    return color;
}

回答by Wylie Kulik

Use hsl()

利用 hsl()

For example in ReactJS (here with CoffeeScript) acting on an SVG text element, though the same technique will work with HTML p/span/h1 etc (color instead of fill property):

例如在 ReactJS(这里使用 CoffeeScript)作用于 SVG 文本元素,尽管同样的技术也适用于 HTML p/span/h1 等(颜色而不是填充属性):

render: ->
              #[... svg setup]
    text
        x: 50
        y: 50
        fontSize: 20
        fill: "hsl(#{@state.el_hue}, #{@state.el_sat}%, @state.el_lum)%"
        "Hello from randomly changing color text."

getInitialState: ->
    el_hue: 0
    el_sat: 0
    el_lum: 0

componentDidMount: ->
    setInterval =>
        @setState
            el_hue: Math.random() * 360
            el_sat: Math.random() * 100
            el_lum: Math.random() * 100
    , 30

       # should do crazy things, change color etc

Here I just did random things with the hsl vals every interval, but you can do anything this way. So you can set up a red to blue transition by just altering the hue values appropriately.

在这里,我只是在每个时间间隔对 hsl val 进行随机操作,但是您可以通过这种方式进行任何操作。所以你可以通过适当地改变色调值来设置红色到蓝色的过渡。