admin管理员组

文章数量:1394544

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?

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?

Share Improve this question edited Jan 29, 2020 at 8:57 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Nov 25, 2015 at 12:10 punkbitpunkbit 7,71710 gold badges57 silver badges95 bronze badges 3
  • 1 Did you try something? Where did you got stuck? – Aramil Rey Commented Nov 25, 2015 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 munity to do your work for you. – arkon Commented Jan 23, 2016 at 18:33
  • jsbin./picuwe/2/edit?js,output – punkbit Commented Jan 25, 2016 at 12:32
Add a ment  | 

2 Answers 2

Reset to default 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.

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

本文标签: javascriptHow to lerp back and forth between two values XY in a loopStack Overflow