CoreAnimation
easing
Swift

CoreAnimationで使うカスタムEasingを自作する

どうも、はぐっです・ω・♪

CoreAnimationでカスタムEasingを使う

CoreAnimation

UIKitのアニメーションよりも自由度が高いアニメーション。
UIKitのアニメーションってさくっと実装できて、非常に便利なのでよく使うんですが、ちょっとかゆいところに手が届かない感を感じることがありました。
こいつは、かなり自由度が高いです、ええ。

Easing(イージング)

アニメーションにおける 変化の仕方
現実世界において、何かが動く時は

  • 始点
  • 終点
  • 動作開始から終了までの時間 (以下、動作時間)

等が必ず存在するわけですが、動作時間が0秒なんてことはほぼほぼないはず。
ということは、始点から終点まで動作時間をかけて「どういう風に」変化するのか、というパラメータがあるはず。
これがまさしくEasing。
ざっくりと変化の仕方の方向性で分けると

  • Linear
    • 常に等速
  • EaseIn
    • 始めはおだやか、後から速く
  • EaseOut
    • 始めは速く、後はおだやか
  • EaseInOut
    • 始めと後はおだやか、途中は速く

の4パターン。
で、それぞれについてさらに変化の仕方の緩急を変えることで、様々なアニメーション実装を行っていくと。

ちなみによく使われるEasingっていうのがあって、それぞれどんな感じの動きかっていうのは
https://easings.net/ja
こちらで確認してみてっ!

で、今回はこのよく使われるEasingをSwiftで実装していく。

Swiftで実装

CoreAnimationを使用するので、QuartzCoreはもちろん必要。
まずは、定義ファイル。適当にどっかに追加してください。

Easing.swift
import QuartzCore

class Easing {

    enum easeIn {
        case sine
        case quad
        case cubic
        case quart
        case quint
        case expo
        case circ
        case back

        var function: CAMediaTimingFunction {
            switch self {
            case .sine  : return CAMediaTimingFunction(controlPoints: 0.47, 0, 0.745, 0.715)
            case .quad  : return CAMediaTimingFunction(controlPoints: 0.55, 0.085, 0.68, 0.53)
            case .cubic : return CAMediaTimingFunction(controlPoints: 0.55, 0.055, 0.675, 0.19)
            case .quart : return CAMediaTimingFunction(controlPoints: 0.895, 0.03, 0.685, 0.22)
            case .quint : return CAMediaTimingFunction(controlPoints: 0.755, 0.05, 0.855, 0.06)
            case .expo  : return CAMediaTimingFunction(controlPoints: 0.95, 0.05, 0.795, 0.035)
            case .circ  : return CAMediaTimingFunction(controlPoints: 0.6, 0.04, 0.98, 0.335)
            case .back  : return CAMediaTimingFunction(controlPoints: 0.6, -0.28, 0.735, 0.045)
            }
        }
    }

    enum easeOut {
        case sine
        case quad
        case cubic
        case quart
        case quint
        case expo
        case circ
        case back

        var function: CAMediaTimingFunction {
            switch self {
            case .sine  : return CAMediaTimingFunction(controlPoints: 0.39, 0.575, 0.565, 1)
            case .quad  : return CAMediaTimingFunction(controlPoints: 0.25, 0.46, 0.45, 0.94)
            case .cubic : return CAMediaTimingFunction(controlPoints: 0.215, 0.61, 0.355, 1)
            case .quart : return CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1)
            case .quint : return CAMediaTimingFunction(controlPoints: 0.23, 1, 0.32, 1)
            case .expo  : return CAMediaTimingFunction(controlPoints: 0.19, 1, 0.22, 1)
            case .circ  : return CAMediaTimingFunction(controlPoints: 0.075, 0.82, 0.165, 1)
            case .back  : return CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.275)
            }
        }
    }

    enum easeInOut {
        case sine
        case quad
        case cubic
        case quart
        case quint
        case expo
        case circ
        case back

        var function: CAMediaTimingFunction {
            switch self {
            case .sine  : return CAMediaTimingFunction(controlPoints: 0.445, 0.05, 0.55, 0.95)
            case .quad  : return CAMediaTimingFunction(controlPoints: 0.455, 0.03, 0.515, 0.955)
            case .cubic : return CAMediaTimingFunction(controlPoints: 0.645, 0.045, 0.355, 1)
            case .quart : return CAMediaTimingFunction(controlPoints: 0.77, 0, 0.175, 1)
            case .quint : return CAMediaTimingFunction(controlPoints: 0.86, 0, 0.07, 1)
            case .expo  : return CAMediaTimingFunction(controlPoints: 1, 0, 0, 1)
            case .circ  : return CAMediaTimingFunction(controlPoints: 0.785, 0.135, 0.15, 0.86)
            case .back  : return CAMediaTimingFunction(controlPoints: 0.68, -0.55, 0.265, 1.55)
            }
        }
    }
}

ほんで、CoreAnimationの実装。
正式に使用するときは、もうちょっと設定するプロパティがあるんですが、ここでは今回の話に出たものに限って記述していくスタイル。

let opacityAnimation                   = CABasicAnimation(keyPath: "opacity")
opacityAnimation.fromValue             = 1.0 // 開始値
opacityAnimation.toValue               = 0.0 // 終了値
opacityAnimation.duration              = 0.1 // 動作時間
opacityAnimation.timingFunction        = Easing.easeInOut.circ.function // Easing

self.layer.add(opacityAnimation, forKey: "opacity")

  

こうして、カスタムEasingを適用したアニメーションを実装することができましたーぱちぱち。