2

使用 setInterval 或 RequestAnimationFrame,我想从 X 和 Y 之间的 lerping 中获取进度值。假设 X 为 0,Y 为 1,我希望它开始时为 0,一半时为 0.5,完成时为 1。

我希望这发生在给定的时间范围内,比如说 5 秒。这意味着当 setInterval/RequestAnimationFrame 达到 2.5 秒时会发生半值 0.5。

最后,我想让它打乒乓球,所以当它达到 5 秒时,值会减少而不是增加,例如 0.9、0.8、0.7 等,然后从 0、0.1、0.2 重新开始......

到目前为止,这是我的代码:

/*
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);

我怎样才能做到这一点?

4

2 回答 2

5

线性插值的基本公式类似于

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

其中XY是要在之间插值的值 和是确定插值程度的t参数;产量和产量。此外,您希望有一些周期长度为 的周期性运动,交替插值方向;这可以通过以下方式实现。如果是一个随时间增长的非负数,计算010X1Y5t

t' = t - t / 10

删除所有以前发生的时期和

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

然后设置

t''' = t' / 5

从一开始就将参数归一化[0,1]并使用基本插值公式。

请注意,此处收集了线性插值和各种其他方法。

于 2015-11-25T12:23:03.120 回答
1

根据您的描述,在任何给定的帧中都有 6 个状态:

  1. 当前 lerp 的开始时间
  2. 勒普时间跨度
  3. 当前方向
  4. 当前时间
  5. 起始值
  6. 终值

从这些您可以计算所需的进度值,例如:

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

对于 requestAnimationFrame,您需要一个简单的回调来传入. 也就是说,函数必须知道它所需要的一切,除了它在运行时可以获得的东西。在这里,当它运行时,它只需要获取当前时间并从那里处理其余部分。

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

但是有一个问题;如果您希望能够使用来自脚本的值或来自屏幕的输入或有关屏幕上元素的最新信息来设置动画,那么您需要能够animableThing在有新的价值观。看,妈妈:

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

我们可以走得更远,通过让调用者传入一个progressValue函数来调用,或者实际上是一个回调,从而使其他类型的事物通用化,这样您就可以获取任何元素、Draw函数和setup函数并制作一个动画,但这是一个合理的起点。

有了上面,我们只需要调用 Mother 来创建一个animableThing函数并用它调用 RequestAnimationFrame。从那时起,它在内部调用 RequestAnimationFrame 以继续循环。

现在,已经完成了,你会想要让它停止,所以在回调中添加一个它可以检查的变量,这样你就可以做到

var animableThing = MotherOfAnimableThings(...);
window.requestAnimationFrame(animableThing);
// ... later
animableThing.stop = true; // it should stop on the next frame
于 2015-11-25T12:44:04.097 回答