2013-10-28 75 views
4

我试图创建一个像Windows 8安装We're getting your PC ready一样的连续颜色转换。我想不出如何编写shift函数。检查所有R,G,B值并将当前颜色与下一种颜色相匹配。连续颜色转换

任何人都可以帮助我吗?或者让我知道是否有比这更好的方法呢?

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

+0

请说明您的具体问题或添加额外的细节,突显正是你需要的。正如目前所写,很难确切地说出你在问什么。 – Marcin

+0

试图更具体。它应该做的。我猜。 – akinuri

+0

注意 - 你有一个失控的'setInterval'循环,你在'switchColor'里面启动一个新的100ms定时器,并且永远不会取消它 - 每个2s你再做一次... – Alnitak

回答

12

检查此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) { 
 
\t var bg \t = getComputedStyle(elm).backgroundColor; 
 
\t \t bg \t = bg.match(/\((.*)\)/)[1]; 
 
\t \t bg \t = bg.split(","); 
 
\t for (var i = 0; i < bg.length; i++) { 
 
\t \t bg[i] = parseInt(bg[i], 10); 
 
\t } 
 
\t if (bg.length > 3) { bg.pop(); } 
 
\t return bg; 
 
} 
 

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

 
// Generates a random RGB value. 
 
function generateRGB(min, max) { 
 
\t var min \t \t = min || 0; 
 
\t var max \t \t = min || 255; 
 
\t var color \t = []; 
 
\t for (var i = 0; i < 3; i++) { 
 
\t \t var num = random(min, max); 
 
\t \t color.push(num); 
 
\t } 
 
\t 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) { 
 
\t var distance = []; 
 
\t for (var i = 0; i < colorArray1.length; i++) { 
 
\t \t distance.push(Math.abs(colorArray1[i] - colorArray2[i])); 
 
\t } 
 
\t 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) { 
 
\t var fps \t \t \t = fps || 30; 
 
\t var duration \t = duration || 1; 
 
\t var increment \t = []; 
 
\t for (var i = 0; i < distanceArray.length; i++) { 
 
\t \t var incr = Math.abs(Math.floor(distanceArray[i]/(fps * duration))); 
 
\t \t if (incr == 0) { 
 
\t \t \t incr = 1; 
 
\t \t } 
 
\t \t increment.push(incr); 
 
\t } 
 
\t 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) { 
 
\t var color = []; 
 
\t for (var i = 0; i < colorArray.length; i++) { 
 
\t \t var hex = colorArray[i].toString(16); 
 
\t \t if (hex.length < 2) { hex = "0" + hex; } 
 
\t \t color.push(hex); 
 
\t } 
 
\t 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 \t \t \t \t = 30; 
 
var duration \t \t = 3; 
 
var transElement \t = document.body; 
 
var currentColor \t = getElementBG(transElement); 
 
var transHandler \t = null; 
 

 
startTransition(); 
 

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

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

+0

这是一个了不起的实现。你能否帮我理解增量停车的目的是什么?还有一点关于它是如何工作的。我试图实现类似的东西,所以你的解释真的会有所帮助! :) –

+0

@NehaAgrawal我试图解释[本页](http://akinuri.com/exps/color-transition/)代码中发生了什么。如果你仍然有问题,我可以更具体。 – akinuri

+0

前一个链接不再有效。这里是新的[链接](http://akinuri.com/exps/color-transition/intro)。 – akinuri

0

哪里是太多的变数? 这种方式呢?

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); 
+0

可以说currentColor是[128,160,200],下一个颜色是[60,140,​​230]。我需要检查currentColor [0]> nextColor [0],如果是的话,我会减少它。如果没有,增加。我需要用所有的r,g,b值来做到这一点。而增加/减少他们需要在一个区间。每次迭代更新元素,以便平滑过渡。当我说变量太多时,我并没有谈论“变量”。我的意思是有很多事情要考虑。 – akinuri

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; 
} 
0

使用hsl()

例如在ReactJS(这里用的CoffeeScript)作用于SVG的文本元素,但同样的技术将与HTML p /跨度/ 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 

在这里,我只是做了每个时间间隔,随机的东西与HSL丘壑,但你可以做这样的任何事情。因此,您可以通过适当更改色调值来设置红色到蓝色的过渡。

0

下面是使用纯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; 
 
}