【问题标题】:How to lerp back and forth between two values X,Y in a loop?如何在循环中在两个值 X,Y 之间来回切换?
【发布时间】:2016-02-28 05:24:34
【问题描述】:

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

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

最后,我希望它是 pingPong,所以当它达到 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);

我该怎么做?

【问题讨论】:

  • 你尝试了吗?你在哪里卡住了?
  • 详细说明@AramilRey 的问题;你试过什么?你做了什么研究?您能否提供 sn-ps 来证明您实际上已经尝试自己解决问题? (一个简单的谷歌搜索就可以为您提供解决这个问题所需的知识。)您不应该依赖 SO 社区为您完成工作。

标签: javascript lerp


【解决方案1】:

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

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

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

t' = t - t / 10

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

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

然后设置

t''' = t' / 5

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

注意,收集了线性插值和其他各种方法here

【讨论】:

【解决方案2】:

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

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

从这些你可以计算出所需的进度值,比如:

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

对于 requestAnimationFrame,you need a simple callback to pass in。也就是说,函数必须知道它所需要的一切,除了它在运行时可以获得的东西。在这里,当它运行时,它只需要获取当前时间并从那里处理其余部分。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 2023-02-21
    • 2021-12-15
    • 1970-01-01
    相关资源
    最近更新 更多