このノートではそれぞれの基本とマウス追従での使い分けを整理します。
DEMO
固定値のlerp
lerp()は二つの値を直線的につなぐ関数です。次のコードでは現在値から目標値までの残り10%を進みます。
function lerp( start, end, progress ) {
return start + ( end - start ) * progress;
}
currentX = lerp( currentX, targetX, 0.1 ); ここで指定している0.1は一秒あたりの割合ではなく一回の更新で進む割合です。30fpsでは一秒間に約30回、120fpsでは約120回実行されるため、高いFPSほど早く目標へ近づきます。
固定値のlerp()をマウス追従に使うと同じ実装でも閲覧環境によって追従の感触が変化します。
経過時間を使うdamp
damp()は前回の描画から経過した秒数を使いそのフレームで進む割合を計算します。
function damp( current, target, speed, deltaTime ) {
return lerp(
current,
target,
1 - Math.exp( -speed * deltaTime )
);
} speedは追従の強さです。値を大きくすると素早く小さくするとゆっくり目標へ近づきます。低FPSでは一回あたり大きく高FPSでは一回あたり小さく進むため同じ時間が経過した時点の位置はほぼ同じになりFPSが違っても追従の感触をそろえられます。
マウス追従での利用
次はポインターのX座標へ要素を追従させる実装です。
const item = document.querySelector( '.item' );
let currentX = 0;
let targetX = 0;
let previousTime = performance.now();
function lerp( start, end, progress ) {
return start + ( end - start ) * progress;
}
function damp( current, target, speed, deltaTime ) {
return lerp(
current,
target,
1 - Math.exp( -speed * deltaTime )
);
}
function update( now ) {
const deltaTime = Math.min( ( now - previousTime ) * 0.001, 0.05 );
previousTime = now;
currentX = damp( currentX, targetX, 8, deltaTime );
item.style.transform = `translateX(${ currentX }px)`;
requestAnimationFrame( update );
}
window.addEventListener( 'pointermove', ( event ) => {
targetX = event.clientX;
}, { passive: true } );
requestAnimationFrame( update ); ポインターが動くたびに変えるのはtargetXだけです。currentXはrequestAnimationFrame()が実行されるたびにtargetXへ近づきます。
固定値のlerp()とdamp()による追従の違いはマウス追従の比較デモで確認できます。
使い分け
一回だけ中間値を求める場合や進捗がすでに0から1へ正規化されている場合はlerp()を使います。
マウス、スクロール、カメラなど変わり続ける目標へ毎フレーム追従させる場合はdamp()を推奨します。FPSが違っても追従速度をそろえやすく、固定値のlerp()を繰り返すより閲覧環境の影響を受けにくいためです。
固定値のlerp()が不適切という話ではありません。注意が必要なのは、固定した補間率を毎フレーム繰り返して追従に使う場合です。