7cc@はてなブログ

JavaScriptとかとか

JavaScript イージング easing

スムーズスクロールにイージング処理をつけたかったので調べた。
ease outとかease inとか。

読み込むファイルが増えるのは面倒なのでライブラリは使用しない。位置の移動や戻す処理を入れるならライブラリを使ったほうがよさそうだけど。

コード

// 1 イージングの関数を用意する。詳しくは下部「イージングについて」のリンク先参照
function easeOut(t, d){
  return 1 - Math.pow(1 - (t / d), 5);
}

// 2 タイマーが重複しない処理を入れる(これはかなりテキトウ)
if(timer){
  clearInterval(timer)
}
var timer

// 3 setIntervalなど定期処理でイージング関数のtに経過時間を代入する
// bが初期値、cが変化量
function smoothScroll(ele, b, c){
  var start = Date.now()
    , total = 1400 // ミリ秒
    , delay = 16 // FPS 16で60FPS, 24で30FPSになる。負荷が無ければ
    , d = 1

  timer = window.setInterval(function() {
    var t = (Date.now() - start) / total
      , result = easeOut(t, d)
      , y = b + result * c
    ele.scrollTop = y
    if (t >= d) { 
      clearInterval(timer)
    }
  }, 16)
}

smoothScroll(document.documentElement, pageYOffset, 511)
// smoothScroll(document.body, body.scrollTop, 511) // Webkit, Quirks-Mode

コードの説明

total timeはミリ秒で設定する。このコードだとスクロール開始から終了まで1.4秒かかる。
引数にeleを指定しているが、ウィンドウのスクロール量を変えるなら24行目をwindow.scrollTo(0, y)で良い。 ページトップにスムーズスクロールするなら、下記のように設定すればよい。

smoothScroll(document.documentElement, pageYOffset, -pageYOffset)

イージングについて

すこし日本語でも説明

イージング関数で使われる言葉

b
beginning value
初期値
c
change in value
変化値
t
time
現在時間
d
duretion
総時間

していること

  1. イージング関数で割合を得て、2. それと変化量をかけ、3. 初期値と足すだけ

総時間は要するに100分率だと考えればよい。

easeOut(10, 100) // 0.4
easeOut(20, 100) // 0.67
easeOut(30, 100) // 0.83
//---------------
easeOut(80, 100) // 0.99968
easeOut(90, 100) // 0.99999
easeOut(100, 100) // 1

最初のpageYOffset(b)が100, 最終的に位置したい場所(c)が1000pxだとすると
1000 * 時間の割合 + 100 でその時に位置すべき値を得ている。
時間が10%の時点で移動は40%、つまり 1000 * 0.4 + 100 = 600
時間が20%の時点で移動は67%、つまり 1000 * 0.67 + 100 = 770
時間が100%の時点で移動は100%、つまり 1000 * 1 + 100 = 1100
(下の説明のほうが分かりやすいかもしれない。)

別のイージング方法

イージングの関数がしていることを、配列で代用することもできる。
割合を配列に代入し、100で割って百分率にする。それを最終的に得たい変化量とかけるだけ。

例 1000px移動する

var percents = [0.1, 0.2, 0.3, 0.5, 0.7, 1, 1.5, 2, 2.5, 3, 4, 5, 7, 
               10, 15, 20, 30, 40, 50, 60, 70, 80, 83, 85,
               90, 92, 93, 94, 95, 96, 97, 98, 98.5, 99, 99.3, 99.5, 99.7, 99.8, 99.9, 100]

var b = pageYOffset
var c = 2000


for(var i = 0, count = 0; i < percents.length; i++){
  setTimeout(function(){
    var easing = percents[count++] / 100
    scrollTo(0, c * easing + b)
  }, i * 30)
}