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


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

![cubiclinear.png](https://qiita-image-store.s3.amazonaws.com/0/8588/8db645f0-9814-ce88-fd1d-5fcb1e28d469.png)

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

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

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

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

![grapher.png](https://qiita-image-store.s3.amazonaws.com/0/8588/b13224db-424c-49a2-0531-a60350ff0d73.png)


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

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

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

![grapher2.png](https://qiita-image-store.s3.amazonaws.com/0/8588/d4875151-a238-5199-57d2-8e5c3d4968fc.png)

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

```swift

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さんより、
等加速度運動（加速）-> 等速直線運動 -> 等加速度運動（減速）
の方が自然ではないか？という意見をいただいたので、こちらも試してみました

```swift

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](https://qiita-image-store.s3.amazonaws.com/0/8588/4f677105-ed53-f520-d383-41f8ecc1d3cf.png)

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

![abc.gif](https://qiita-image-store.s3.amazonaws.com/0/8588/79462879-124f-8783-9de1-a0588ebf6b37.gif)

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

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

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

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