How to reverse a animation loop that pulls values from an array

I’m having difficulty figuring out how to reverse the direction of an animated loop when it reaches the end of an array. I found an example online that creates two “states” with an additive and subtractive loop within each respective state, but I have not been able to implement it.

Below is a simplified example of what I’m attempting. Would be grateful for someone’s advice on how to reverse the direction of the point when the last value within the array (14) is reached and, then, how to reverse it again when it returns to the array’s first value.

let array1 = [];
let a = 0;
let i = 0;

function setup() {
  createCanvas(400, 200);
  array1 = [2,4,6,8,10,12,14];
}

function draw() {
  background(220);
  frameRate(1);
  strokeWeight(10);
  stroke(255);
  point(array1[i]*20,100);
  i=i+1;
}
  • I see you’ve declared a variable named a but you’re not using it.
  • Indeed you need 2 variables: 1 for the array’s current index and the other for the current step the index increases or decreases.
  • Just invert the 2nd variable’s sign when the index reaches either 0 or the array’s length - 1.
const arr = Uint8Array.from({ length: 7 }, (_, idx) => idx + 1 << 1);
var idx = 0, dir = -1;

function loop() {
  console.log(idx, dir, arr[idx]);
  if (idx == 0 || idx == arr.length - 1)  dir *= -1;
  idx += dir;
}

setInterval(loop, 2000);

“idx == 0 || idx == arr.length - 1”
That’s beautiful. Thanks!

1 Like

Previous solutions are fine and answer to your question.
To open up to other solutions, i often prefer to use a generator:

function * loopOnArray(someArray){
 while (true) {// infinite loop 
  for (let aPoint of someArray){
    yield aPoint
  }
// once done reverse the array 
  someArray= someArray.reverse();
 } // and loop again (while true)
}

Within an infinite loop (while true) this generator distributes points one per one (each yield) from the array and, once done, reverse this array and leaves for a new round.

To use it :
Instantiate the generator in setup with a global var and give it the array we want to act on as the parameter.

let giveAPoint;

function setup() {
  createCanvas(400, 200);
  array1 = [2,4,6,8,10,12,14];
  giveAPoint = loopOnArray(array1);  // instantiate the generator with our array 
}

Then in draw, ask at each round for a new point :

function draw(){
  background(220);
  frameRate(1);
  strokeWeight(10);
  stroke(255);
  point(giveAPoint.next().value*20,100);
}

There is many advantages to externalize loops in a generator : no complicated indices or comparison in the draw loop , can easily limit to a fixed or variable number of rounds (by replacing while true) etc.

One more info: you can reuse as many time you want the generator function as long as you create new instances.

HTH

  • Your generator function* approach is a nice idea.
  • Problem is it’s gonna repeat the head or tail elements for each reverse().
  • That is, we would see the tail value 14 twice & head value 2 twice in a row.
  • Also, method Array::reverse() mutates the array in place.
  • Just someArray.reverse(); is enough. No need to reassign it back to the same array.

I’ve converted my original solution to use a function* generator: :man_genie:

const
  arr = Uint8Array.from({ length: 7 }, (_, idx) => idx + 1 << 1),
  eternalArray = createInfiniteArrayLoop(arr);

function* createInfiniteArrayLoop(arr) {
  var idx = 0, dir = -1;

  while (true) {
    yield arr[idx];
    if (idx == 0 || idx == arr.length - 1)  dir *= -1;
    idx += dir;
  }
}

function loop() {
  console.log(eternalArray.next().value);
}

setInterval(loop, 2000);

Same as above, but using for ( ; ; ) in place of while () for a shorter code:

const
  arr = Uint8Array.from({ length: 7 }, (_, idx) => idx + 1 << 1),
  eternalArray = createInfiniteArrayLoop(arr);

function* createInfiniteArrayLoop(arr) {
  for (var idx = 0, dir = -1; ; idx += dir) {
    yield arr[idx];
    if (idx == 0 || idx == arr.length - 1)  dir *= -1;
  }
}

function loop() {
  console.log(eternalArray.next().value);
}

setInterval(loop, 2000);

You’re right GoToLoop:
1: no need to reassign the array.
2: if you don’t want to repeat borders can’t use for … in , must use indices too :

function * loopOnArray(someArray){
let i=0;
while (true){
 while (i<someArray.length) { 
  console.log(someArray[i])
    yield someArray[i];
    i+=1;
  }
  someArray.reverse();
  i=1; // skip border
 }
}
1 Like