ヌルヌルっとスムーズスクロールの巻

smorgasbord は、F5キーを乗っ取って、独自の関数を呼ぶことで動的にレスを追加しています。せっかくなので、キーボードを活用したいところ。ということで、スペースキーが押下されたときは一番上に表示されているレスの次までスクロールする仕様にしました。同じように、右カーソルキーでも次のレスまでスクロール、左カーソルキーで逆に前のレスまでスクロールすることにしました。

ここで気づいたのは、普通に下カーソルキーでスクロールした場合、ズズッとスムーズスクロールされるのですが、単にscrollTo()を呼ぶだけだと、バチッとスクロールされてしまうので、どこからどこまでスクロールされたのかが視覚的に分かりづらい、ということ。せっかくなので、iPhone みたいにヌルヌルっと加速するスクロールをしてみたい。

が。どっから手をつけていいかわからない。あてもなく「加速度 スクロール」でググってみる。すると「等加速度運動」というキーワードを発見。なんだか一度高校で習った気がしないでもないが、文系なので全く覚えていない(致命的)。本棚から物理の参考書を探し出すと、さっそく公式を発見。

位置の公式: x = x0 + v0t + 1/2at2

うーん、この単元は高一の一番始めだったかもしれない。

とにかく、今回の用途では、現在のスクロール位置 (window.pageYOffset) から指定の場所までヌルヌルとスクロールさせたい。初速度は当然ゼロ。中点に来たところで、最高速度に達する。スクロールする距離に関わらず、一定の時間でスクロールさせないとユーザビリティに支障をきたすので、まずは適切な加速度を求めてあげなければならない。ここから先は、どうにかして中学生の数学の知識を必死に掘り返すしかない。とりあえず、上の公式を加速度 a についての方程式に式変形すると、

1/2at2 = x - x0 - v0t
a = 2(x - x0 - v0t) / t2

となる。初期位置 x0 と 初速度 v0 を無視すれば

a = 2x / t2

となる。位置 x に中点、時間 t に 中点に到達するまでの時間を代入してあげれば、加速度 a が求まる。あとは、最初の公式でその加速度を使い、スクロールする分割数を t として、ループを回してインクリメントしていけば位置が求まる。中点まで来たら、終点位置から (1/2)at2 を引いて、t をデクリメントしていきブレーキをかける。とりあえずコードを。

function smoothScrollTo(y) {
    var y1 = window.pageYOffset;
    var y2 = y
    if (y2 < 0) y2 = 0;
    if ((y2 + window.innerHeight) > document.body.offsetHeight)
        y2 = document.body.offsetHeight - window.innerHeight;
    var delta = y2 - y1;
    var steps = 16;
    var a = (delta / 2) * 2 / Math.pow(steps / 2, 2);
    var ha = a / 2;
    for (var x = 0; x < steps / 2; ++x) {
        window.scrollTo(0, y1 + (ha * Math.pow(x, 2)));
    }
    for (var x = steps / 2 - 1; x >= 0; --x) {
        window.scrollTo(0, y2 - (ha * Math.pow(x, 2)));
    }
    window.scrollTo(0, y2);
}

ふぅ。なんとか実装できたみたい。PageDown, PageUp, Home, End なんかにもこれを適用してあげるとヌルヌルになっていい感じだ。同じようなことをする Firefox拡張機能があったような気がしないでもないけど。