4

The behaviour I want is this: The background color changes to say, gold, and remains that color for say X length of time. Then, background color changes to say, red, and remains that color for say Y length of time. The background color then changes back to gold and remains that color for X length of time. Then the background color changes back to red and stays that way for Y length of time. This whole kit and caboodle executes in a loop-style fashion for Z number of times and then ends.

I've tried putting setInterval'd functions into a for loop (in order to count the number of times we make the change) but have found that all of the functions that have been set to setInterval themselves all start running the interval timers at the same time (not in sequence).

I hope this is clear. Here is a JSFiddle of my efforts: http://jsfiddle.net/6WE6s/3/ I've managed to get the background color to change in a even pattern, but I want the pattern described above and I'm confused as to what to do next.

Thanks in advance for the help! :)

jonathanbell
  • 2,507
  • 5
  • 23
  • 40
  • Maybe use `setTimeout()` instead, and have each iteration call `setTimeout()` with the specific number it needs (or not at all, if the cycle is done). – Tim M. Sep 07 '12 at 06:25
  • Or use `setInterval` and keep count (use a closure) then call `clearInterval` to cancel. – RobG Sep 07 '12 at 06:37
  • Look for my edit. I think, now is as you wish :) – abuduba Sep 07 '12 at 12:08
  • Wow, I can't believe how many people responded to this question! Thank you all very much. I'm sorry that it has taken me the weekend to reply - I've been busy. This is truly an example of "ask 5 programmers to solve a problem, get five different code bases". I see now that setInterval may not be the best choice here (at least for the looping as it loops in a regular fashion). I felt this may be the response. Also, sorry to those who were wondering about jQuery. I should have mentioned that I don't care if the answer uses it or not. Thanks all, I wish I could give more points to more people. – jonathanbell Sep 10 '12 at 15:41

7 Answers7

2
var colors = [
  ['gold', 2000],  // X = 2000 miliseconds
  ['red', 1000]   // Y = 1000
],
repeat = 3, // Z = 3,
index  = 0, // current position in colors array
changeColor = function( ) {

   // if index == colors.length then mod = 0 
   var mod = index % colors.length;

   if(!index || mod || --repeat ) {
     index = mod;
     var data = colors[ index++  ];  // data = [ currentColor, currentColorTimeout ]
     document.body.style.background = data[0];
     setTimeout( changeColor, data[1] ); // and so on
  }
  //if index >0 && index == last 
  //then decrement `repeat` and check if is == 0
  //nothing to do :)

};
changeColor(); // run

This is a simple example. You can make function with arguments(colors,repeats) and its body as above.

Note: setInterval isn't suitable for this purpose because in setInterval you pass timeout once

If repeat initially is 0 will be an infinite number of repetitions

abuduba
  • 4,986
  • 7
  • 26
  • 43
  • I thought OP requested a limited number of iterations. – Fabrício Matté Sep 07 '12 at 07:03
  • @FabrícioMatté hmm, yes he did. – Alnitak Sep 07 '12 at 07:12
  • Thank you very much, your code is lean and mean. Now to start searching for a way (callback?) to signal the completion of all the iterations... If repeat is zero and index == last? – jonathanbell Sep 10 '12 at 16:30
  • completion is in time where condition in function returns false otherwise changeColor is invoking again. Try: `if(!index || mod || --repeat ) { ... } else{ yourOnCompleteCallback() }`; – abuduba Sep 10 '12 at 19:14
1

Don't use setInterval(). With setTimeout() you can do something like this:

function changeColors(colors, repeats) {
    var i = 0;
    if (typeof repeats === "undefined")
        repeats = 1;
    function doNext() {
        if (i >= colors.length){
            if (--repeats > 0)
                i = 0;
            else
                return;
        }
        $('body').css('background-color', colors[i].color);
        setTimeout(doNext, colors[i++].delay);
    }
    doNext();
}

changeColors([{color : "gold", delay : 2000},
             {color : "red", delay : 4000}],
             3);

You can add as many colours as you like, each with their own delay, by adding more elements to the array you pass to changeColors(). The function will go through the colours in turn, and do the whole sequence the number of times specified in the repeats parameter.

Demo: http://jsfiddle.net/nnnnnn/6WE6s/10/

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • **@nnnnnn:** `repeats = repeats || 1` – elclanrs Sep 07 '12 at 06:51
  • @elclanrs - Yeah, I know. When I first started writing the function and included that undefined test I was allowing for a call where `0` was passed in and the whole function shouldn't run, except that of course the way it ended up when I posted it it doesn't actually check the repeats value until after going through the array once... Oh well. I leave any further tidy-up as an exercise for the reader... – nnnnnn Sep 07 '12 at 06:56
  • 1
    @nnnnnn IMHO, a value of zero should have represented "run forever". – Alnitak Sep 07 '12 at 07:11
  • @Alnitak - That's a great idea that I didn't think of at the time. (Still, I'm not going to go back and change it now - I think the OP has more than enough options to choose from and can add such tweaks as desired.) – nnnnnn Sep 07 '12 at 07:14
  • p.s. the OP didn't mention jQuery... And **all** of the current answers either assume it, or are incomplete. – Alnitak Sep 07 '12 at 07:15
1

Here's my effort - no jQuery required:

function colorCycle(el, count, cols) {
    var i = 0,
        n = cols.length;

    // allow this to work on any element given its ID
    el = (typeof el === "string") ? document.getElementById(el) : el;


    if (n === 0) {
        return;       // no colours?
    } else if (n === 1) {
        count = 1;    // don't trigger any timers if there's only one colour
    }

    // all of the hard work is done here
    (function repeat() {

        var interval = cols[i][1];
        el.style.backgroundColor = cols[i][0];

        // only do the whole cycle "count" times - 0 = forever
        if (++i === n) {
            if (count && !--count) {
                return;
            }
            i = 0;
        }
        setTimeout(repeat, interval);  // call myself
    })();  // IIFE starts the cycle straight away
};

colorCycle(document.body, 5, [
    ['red', 1000],
    ['gold', 500]]);

See http://jsfiddle.net/alnitak/42PeT/

Alnitak
  • 334,560
  • 70
  • 407
  • 495
0

try this

var colors = []; 
colors.push({color:"gold", time:4000}); //4000 X length of time
colors.push({color:"red", time:2000}); //2000 Y length of time

var numberofTimes = 50; //50 Z number of times
var $body;
var times = 0; // counter for tracking
var currentColor = {}; //currentColor info can be used to get the current

$(function(){
    $body = $('body');
    changeBG();
});

function changeBG()
{
    currentColor  =  colors[times % colors.length];
    $body.css('background-color',currentColor.color);
    times++;

    if(times<numberofTimes)
       setTimeout(changeBG, currentColor.time);
}

check this quick DEMO

Yograj Gupta
  • 9,811
  • 3
  • 29
  • 48
  • 1
    whether it works or not, this code is not nice. The `changeBG` function has a hardcoded element, a hard coded number of counts. At the top there's an over-complicated way of creating the required `color` object. – Alnitak Sep 07 '12 at 06:38
0

Abstain from using setInterval. Reference here.

EDIT: I've missed the different delay in calls.

var colors = ["#FF0000", "#00FF00", "#0000FF"];
var times  = [1000, 2000, 3000];
var backgroundColor = "";
var counter = 0;

var changeBackground = function () {

    // if we ran out of colors — do nothing: this simply goes out
    // of the function, without continually calling setTimeout.
    if (counter >= colors.length)
        return;

    // you fetch your new color here and increase the counter
    // The counter keeps count of how many animations you've done.
    backgroundColor = colors[counter];

    // increase the counter to point to the next index of colors
    // array you'll use in a subsequent call
    counter++;

    // do your magic voodoo change background animation here.
    // I'm just doing a console.log() to be sure this works.
    // Your question was framework agnostic, the answer should be too.
    console.log(backgroundColor);

    // setInterval to repeat
    window.setTimeout(changeBackground, times[counter]);
}

window.setTimeout(changeBackground, times[counter]);
Community
  • 1
  • 1
joncys
  • 1,300
  • 11
  • 25
0

A basic example iterating an array of color and time arrays with setTimeout.

(function() {
    var i = 0,
        colorsTimes = [['gold', 'red', 'gold', 'red', 'gold'],
                       [2000, 4000, 2000, 4000, 2000]];

    function switchColors() {
        setTimeout(function() {
            $('body').css('background-color', colorsTimes[0][i]);
            if (++i < colorsTimes[0].length) switchColors();
        }, colorsTimes[1][i]);
    }
    switchColors();
}());

Fiddle

Fabrício Matté
  • 69,329
  • 26
  • 129
  • 166
-1

Using setTimeout:

var doCount = (function() {
    var count = 0;
    var interval;
    var limit = 5;  // default

    return function(num) {
      limit = num || limit;

      if (count < limit) {
        count++;
        console.log('running number ' + count);
        interval = setTimeout(arguments.callee, 1000);

      } else {
        interval && clearTimeout(interval);
      }
    }

}())

Using setInterval:

var doCount = (function() {
    var count = 0;
    var interval;
    var limit = 5;  // default

    return function(num) {
      limit = num || limit;

      if (interval) {

        if (++count >= limit) {
          interval && clearInterval(interval);
        }
        console.log('running number ' + count);

      } else {
        interval = setInterval(arguments.callee, 1000);
      }
    }

}())

The advantage of setTimeout is that you can adjust the time between runs to make it more regular, setInterval just tries to run as regularly as it can.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • 1
    `arguments.callee` is deprecated in ES5 strict mode - just give the function expression a name instead... – Alnitak Sep 07 '12 at 06:40
  • BTW, this code only demonstrates how to run a timer event a specific number of times. It in no way addresses the OP problems of having _different_ inter-event timings. – Alnitak Sep 07 '12 at 10:57
  • @Alnitak—arguments.callee isn't deprecated in ES5 — in non–strict mode it's business as usual. In strict mode it's not available, attempting access throws an error, I'm sure the OP can sort that in the unlikely case that strict mode is being used. More important is that named function expressions are buggy in IE. Regarding timing, see the last paragraph. Anyhow, the answer is just examples of using setTimeout and setInterval. – RobG Sep 07 '12 at 15:02