アニメーションなどでよく使われる便利なイージング関数ですが、
普通のイージング関数は、終始曲線になっています。
ですが突発的に、最初と最後だけイーズをかけて、中間は線形になるような関数が欲しくなるケースがありました。
最初と最後だけにイージングをかけたくなるケースって結構ある気がします。
以下のような関数です。
しかしいい感じなのが見当たらなかったので、
作ることにしました。
基本的に真ん中に線形な関数を挟み込むだけなので、
場合分けでできそうです。
今回は数式がシンプルな、Cubic In Outでいきます。
f(x) = 3x^2 - 2x^3
なめらかにCubicの式と線形の式を繋げたいので、真ん中に差し込む式の勾配を求めたいです。f(0.5)の時の勾配ですね。
なのでCubicの関数を微分します。
f'(x) = 6x - 6x^2
で、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)
}
...あまり違いがわからないので、アニメーションにしてみます。
上がcubic、下が放物線を使ったものです。
上の方がよりUIアニメーションっぽく、
下の方が物理的な力を感じられるようなアニメーションになった・・・ような・・・?
放物線は、結構身近でよく目にする動きなので、意外と敏感に区別できることからそう感じるのかもしれません。
こちらも場面によってはこれだ!となるような気がします
koherさん、ありがとうございました!