2

Using setInterval or RequestAnimationFrame, I'd like to get the progression value from lerping between X and Y. Assuming that X is 0 and Y is 1, I want to have 0 when it starts, 0.5 in the half, and 1 when finished.

I'd like this to happen in a given timeframe, let's say 5 seconds. Meaning that the half value 0.5 would happen when the setInterval/RequestAnimationFrame reaches 2.5seconds.

Finally, I'd like it to pingPong, so when it reaches the 5 seconds the values are decreasing and not increasing, such as 0.9, 0.8, 0.7, etc and then start again from 0, 0.1, 0.2...

Here is my code so far:

/*
function lerp(start, end, time) {
    return start * (1.0 - time) + end * time;
}
*/
function lerp (start, end, amt){
  return (1-amt)*start+amt*end;
}

function repeat(t, len) {
  console.log('t: ' + t + ', len: ' + len);
    return t - Math.floor(t / len) * len;
}

function pingPong(t, len) {
    t = repeat(t, len * 2);
    return len - Math.abs(t-len);
}

var transitionDuration = 1;
var startTime = Date.now()/1000;
var startPos = 0;
var endPos = 1;

setInterval(function () {
    var currentTime = Date.now()/1000;
  console.log('currentTime:', currentTime);
    var adjustedTime = pingPong(currentTime-startTime, transitionDuration);
    var x = lerp(startPos, endPos, adjustedTime);

    console.log(Math.abs(x.toFixed(2)));

}, 100);

How can I do this?

halfer
  • 19,824
  • 17
  • 99
  • 186
punkbit
  • 7,347
  • 10
  • 55
  • 89
  • 1
    Did you try something? Where did you got stuck? – Aramil Rey Nov 25 '15 at 12:15
  • 1
    To elaborate on what @AramilRey asked; what have you tried? What research have you done? Can you provide snippets to demonstrate that you've actually tried to solve the problem yourself? (A simple google search would have provided you with the knowledge needed to tackle this question.) You shouldn't be relying on the SO community to do your work for you. – arkon Jan 23 '16 at 18:33
  • http://jsbin.com/picuwe/2/edit?js,output – punkbit Jan 25 '16 at 12:32

2 Answers2

5

The basic formula for linear interpolation would be something like

InterpolatedValue = X*t + Y*(1-t)

where X and Y are the values to be interpolated between and t is a parameter between 0 and 1 determining the degree of interpolation; 0 yields X and 1 yields Y. Furthermore, you would like to have some periodic movement with a period length of 5, alternating the direction of interpolation; this can be achieved as follows. If t is a nonnegative number growing over time, calculate

t' = t - t / 10

to remove all previous periods which have occured and

t'' = t'     : t' in [0,5)
      5 - t' : t' in [5,10)

and afterwards set

t''' = t' / 5

to normalize the parameter into [0,1] and use the basic interpolation formula from the beginning.

Note that linear interpolation and various other methods are collected here.

Codor
  • 17,447
  • 9
  • 29
  • 56
  • I've been playing with it, but I can't figure out how to make it bounce between 0 and 400. I created a codepen here - https://codepen.io/anon/pen/JxOYXV - may you please help me to get this "t" to bounce between 0 and 1. Here is a gist - https://gist.github.com/Noitidart/5cbfb4af8b2df582ddd703c878b266d6 – Noitidart Feb 06 '19 at 17:04
0

From your description, at any given frame there are 6 pieces of state:

  1. Start time of current lerp
  2. Lerp timespan
  3. Current direction
  4. Current time
  5. Start value
  6. End value

From these you can calculate the required progress value, say:

function progressValue(startTime, lerpSpanSeconds, dir, 
                       currentTime X, Y, dir, currentTime) {
    // lerp
    return 0.42;
}

For requestAnimationFrame, you need a simple callback to pass in. That is, the function has to know everything it needs except what it can acquire when it runs. Here, when it runs it just needs to get the current time and work the rest out from there.

function animableThing() {
    var startTime = 7;
    var lerpSpanSeconds = 3;
    var dir = +1;
    var X = 0;
    var Y = 1;
    var currentTime = GetCurrentUnicornTime();
    var thingToAnimate = document.getElementById('myAnimableElement');

    var progress = progressValue(startTime, lerpSpanSeconds, dir, 
          currentTime, X, Y, dir, currentTime);
    // reverse when we hit the end
    if(progress > Y) {
        dir *= -1;
        startTime = currentTime;
        progress = Y;
    }

    DrawAnimationThing(thingToAnimate, progress);

    // continue the animation
    window.requestAnimationFrame(animableThing);
}

But there's a problem; if you want to be able to set up the animation using values from the script or inputs from the screen, or up-to-date information about the elements on the screen, then you need to be able to make an animableThing callback fresh when you have new values. Behold, the mother:

function MotherOfAnimableThings(startTime, lerpSpanSeconds, dir, X, Y, 
   thingToAnimate)
{
    // Passed in variables have scope outside the animableThing, these
    // will be private to the animableThing function.
    // Consider defaulting or validation here

    // Construct a new function freshly each time the Mother is called,
    // and return it to the caller. Note that we assign a variable here
    // so that we can re-call RequestAnimationFrame to continue the loop
    var callback = (function() {
        var currentTime = GetCurrentUnicornTime();
        var progress = progressValue(startTime, lerpSpanSeconds, dir, 
              currentTime, X, Y, dir, currentTime);
        // reverse when we hit the end
        if(progress > Y) {
            dir *= -1;
            startTime = currentTime;
            progress = Y;
        }

        DrawAnimationThing(thingToAnimate, progress);

        window.requestAnimationFrame(callback);
    });
    return callback;
}

We could go further, and make this general for other types of thing by letting the caller pass in a progressValue function to call, or in fact a callback, so that you could take any element, Draw function and setup function and make a thing that animates, but this is a reasonable starting point.

With the above, we just need to call Mother to create an animableThing function and call RequestAnimationFrame with that. From then on, it calls RequestAnimationFrame internally to continue the cycle.

Now, having done that, you will want to make it stop, so add in a variable in the callback which it can check, so that you can do

var animableThing = MotherOfAnimableThings(...);
window.requestAnimationFrame(animableThing);
// ... later
animableThing.stop = true; // it should stop on the next frame
Phil H
  • 19,928
  • 7
  • 68
  • 105