7
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

真ん中が線形になるイージング関数

Last updated at Posted at 2015-07-10

アニメーションなどでよく使われる便利なイージング関数ですが、
普通のイージング関数は、終始曲線になっています。
ですが突発的に、最初と最後だけイーズをかけて、中間は線形になるような関数が欲しくなるケースがありました。
最初と最後だけにイージングをかけたくなるケースって結構ある気がします。
以下のような関数です。

cubiclinear.png

しかしいい感じなのが見当たらなかったので、
作ることにしました。

基本的に真ん中に線形な関数を挟み込むだけなので、
場合分けでできそうです。

今回は数式がシンプルな、Cubic In Outでいきます。

f(x) = 3x^2 - 2x^3

grapher.png

なめらかにCubicの式と線形の式を繋げたいので、真ん中に差し込む式の勾配を求めたいです。f(0.5)の時の勾配ですね。

なのでCubicの関数を微分します。

f'(x) = 6x - 6x^2

grapher2.png

で、0.5を代入すると、勾配は1.5であることがわかります。
あとは頭とお尻を接続すればなめらかにつなげることができます。
ではPlaygroundで実装してみましょう。


import XCPlayground

func cubic(x: Double) -> Double {
    let xx = x * x
    let xxx = xx * x
    return 3.0 * xx - 2.0 * xxx
}

func cubic_with_linear(var x: Double, linearRatio: Double = 0.5) -> Double {
    let linerPart = linearRatio / (1.0 - linearRatio)
    if linerPart.isInfinite {
        return x
    }
    let cubicPart = 1.0
    let width = 1.0 + linerPart
    let slope = 1.5
    let height = 1.0 + linerPart * slope

    x *= width
    
    if x < cubicPart * 0.5 {
        return cubic(x) / height
    }

    x -= cubicPart * 0.5
    var floorValue = 0.5
    
    if x < linerPart {
        return (floorValue + slope * x) / height
    }
    
    x -= linerPart
    floorValue += slope * linerPart
    
    return (floorValue + cubic(0.5 + x) - 0.5) / height
}

let N = 200
let step = 1.0 / Double(N - 1)

for i in 0..<N {
    let x = step * Double(i)
    let y = cubic(x)
    XCPCaptureValue("cubic y", y)
}


for i in 0..<N {
    let x = step * Double(i)
    let y = cubic_with_linear(x)
    XCPCaptureValue("y", y)
}

関数を普通に並べて、x, yともに0~1になるようにつじつま合わせを行って完了です。
linearRatioは全体から見ての線形部の割合です。

追記
koherさんより、
等加速度運動(加速)-> 等速直線運動 -> 等加速度運動(減速)
の方が自然ではないか?という意見をいただいたので、こちらも試してみました


func parabola_easing(x: Double) -> Double {
    if x < 0.5 {
        return x * x * 2.0
    }
    return -1.0 + 2.0 * x * (2.0 - x)
}
func parabola_easing_with_linear(var x: Double, linearRatio: Double = 0.5) -> Double {
    let linerPart = linearRatio / (1.0 - linearRatio)
    if linerPart.isInfinite {
        return x
    }
    let parabolaPart = 1.0
    let width = 1.0 + linerPart
    let slope = 2.0
    let height = 1.0 + linerPart * slope
    
    x *= width
    
    if x < parabolaPart * 0.5 {
        return parabola_easing(x) / height
    }
    
    x -= parabolaPart * 0.5
    var floorValue = 0.5
    
    if x < linerPart {
        return (floorValue + slope * x) / height
    }
    
    x -= linerPart
    floorValue += slope * linerPart
    
    return (floorValue + parabola_easing(0.5 + x) - 0.5) / height
}

let N = 200
let step = 1.0 / Double(N - 1)

for i in 0..<N {
    let x = step * Double(i)
    let y = parabola_easing(x)
    XCPCaptureValue("cubic y", y)
}

for i in 0..<N {
    let x = step * Double(i)
    let y = parabola_easing_with_linear(x)
    XCPCaptureValue("y", y)
}

parabola.png

...あまり違いがわからないので、アニメーションにしてみます。

abc.gif

上がcubic、下が放物線を使ったものです。

上の方がよりUIアニメーションっぽく、
下の方が物理的な力を感じられるようなアニメーションになった・・・ような・・・?
放物線は、結構身近でよく目にする動きなので、意外と敏感に区別できることからそう感じるのかもしれません。

こちらも場面によってはこれだ!となるような気がします

koherさん、ありがとうございました!

7
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?