Skip to content

Commit

Permalink
Add spring to transform animation
Browse files Browse the repository at this point in the history
Add chain function

Replace spring with elastic

Return value func for transform

Remove chain

ccee

Add elasticity paramenter
  • Loading branch information
f3dm76 committed Aug 20, 2018
1 parent 80170a9 commit 24c8e0b
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 106 deletions.
12 changes: 1 addition & 11 deletions Example/Example/Examples/Animations/EasingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EasingView: MacawView {

for (i, easing) in all.enumerated() {
let y = Double(150 + i * 75)
let title = EasingView.title(easing: easing)
let title = String(describing: type(of: easing))
let titleText = Text(text: title, align: .mid, place: .move(dx: centerX, dy: y - 45))

let fromCircle = Circle(cx: centerX - 100, cy: y, r: 25).stroke(with: fromStroke)
Expand All @@ -34,14 +34,4 @@ class EasingView: MacawView {
animation = animations.combine().cycle()
super.init(node: circlesNodes.group(), coder: aDecoder)
}

fileprivate static func title(easing: Easing) -> String {
switch easing {
case .ease: return "Ease"
case .linear: return "Linear"
case .easeIn: return "Ease In"
case .easeOut: return "Ease Out"
case .easeInOut: return "Ease InOut"
}
}
}
6 changes: 0 additions & 6 deletions Macaw.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
57614B0A1F83D15600875933 /* DoubleInterpolation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0EC1E3B393900D1CB28 /* DoubleInterpolation.swift */; };
57614B0B1F83D15600875933 /* PathSegmentType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E12F1E3B393900D1CB28 /* PathSegmentType.swift */; };
57614B0C1F83D15600875933 /* AnimatableVariable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0E21E3B393900D1CB28 /* AnimatableVariable.swift */; };
57614B0D1F83D15600875933 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FD1E3B393900D1CB28 /* TimingFunction.swift */; };
57614B0E1F83D15600875933 /* AnimationCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0F71E3B393900D1CB28 /* AnimationCache.swift */; };
57614B0F1F83D15600875933 /* Transform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1361E3B393900D1CB28 /* Transform.swift */; };
57614B101F83D15600875933 /* Graphics_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = A718CD4A1F45C28F00966E06 /* Graphics_macOS.swift */; };
Expand Down Expand Up @@ -157,7 +156,6 @@
57E5E1661E3B393900D1CB28 /* TransformHashable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FA1E3B393900D1CB28 /* TransformHashable.swift */; };
57E5E1671E3B393900D1CB28 /* MorphingGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */; };
57E5E1681E3B393900D1CB28 /* OpacityGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */; };
57E5E1691E3B393900D1CB28 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FD1E3B393900D1CB28 /* TimingFunction.swift */; };
57E5E16A1E3B393900D1CB28 /* TransformGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */; };
57E5E16B1E3B393900D1CB28 /* AnimationSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E0FF1E3B393900D1CB28 /* AnimationSequence.swift */; };
57E5E16C1E3B393900D1CB28 /* CombineAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E5E1001E3B393900D1CB28 /* CombineAnimation.swift */; };
Expand Down Expand Up @@ -579,7 +577,6 @@
57E5E0FA1E3B393900D1CB28 /* TransformHashable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformHashable.swift; sourceTree = "<group>"; };
57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MorphingGenerator.swift; sourceTree = "<group>"; };
57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpacityGenerator.swift; sourceTree = "<group>"; };
57E5E0FD1E3B393900D1CB28 /* TimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingFunction.swift; sourceTree = "<group>"; };
57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformGenerator.swift; sourceTree = "<group>"; };
57E5E0FF1E3B393900D1CB28 /* AnimationSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationSequence.swift; sourceTree = "<group>"; };
57E5E1001E3B393900D1CB28 /* CombineAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineAnimation.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1087,7 +1084,6 @@
57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */,
57A27BD21E44C5570057BD3A /* ShapeAnimationGenerator.swift */,
57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */,
57E5E0FD1E3B393900D1CB28 /* TimingFunction.swift */,
57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */,
);
path = animation_generators;
Expand Down Expand Up @@ -1963,7 +1959,6 @@
57614B0B1F83D15600875933 /* PathSegmentType.swift in Sources */,
57614B0C1F83D15600875933 /* AnimatableVariable.swift in Sources */,
5B6E193C20AC58F900454E7E /* Effect.swift in Sources */,
57614B0D1F83D15600875933 /* TimingFunction.swift in Sources */,
57614B0E1F83D15600875933 /* AnimationCache.swift in Sources */,
57614B0F1F83D15600875933 /* Transform.swift in Sources */,
57614B101F83D15600875933 /* Graphics_macOS.swift in Sources */,
Expand Down Expand Up @@ -2096,7 +2091,6 @@
57E5E15B1E3B393900D1CB28 /* DoubleInterpolation.swift in Sources */,
57E5E1961E3B393900D1CB28 /* PathSegmentType.swift in Sources */,
57E5E1531E3B393900D1CB28 /* AnimatableVariable.swift in Sources */,
57E5E1691E3B393900D1CB28 /* TimingFunction.swift in Sources */,
5B6E193B20AC58F900454E7E /* Effect.swift in Sources */,
57E5E1631E3B393900D1CB28 /* AnimationCache.swift in Sources */,
57E5E19D1E3B393900D1CB28 /* Transform.swift in Sources */,
Expand Down
11 changes: 5 additions & 6 deletions Source/animation/AnimationProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class AnimationProducer {
self.addAnimation(next)
}
})

case .opacity:
addOpacityAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
if let next = animation.next {
Expand Down Expand Up @@ -306,10 +305,10 @@ class AnimationProducer {

let startDate = Date(timeInterval: contentsAnimation.delay, since: Date())

var unionBounds: Rect? = .none
if let startBounds = contentsAnimation.getVFunc()(0.0).group().bounds,
let endBounds = contentsAnimation.getVFunc()(1.0).group().bounds {
unionBounds = startBounds.union(rect: endBounds)
var unionBounds = contentsAnimation.getVFunc()(0.0).group().bounds
stride(from: 0.0, to: 1.0, by: 0.02).forEach { progress in
let t = animation.easing.progressForTimingFunction(progress: progress)
unionBounds = unionBounds?.union(rect: contentsAnimation.getVFunc()(t).group().bounds!)
}

guard let layer = cache?.layerForNode(node, animation: contentsAnimation, customBounds: unionBounds) else {
Expand Down Expand Up @@ -379,7 +378,7 @@ class AnimationProducer {
continue
}

let t = progressForTimingFunction(animation.easing, progress: progress)
let t = animation.easing.progressForTimingFunction(progress: progress)
group.contents = animation.getVFunc()(t)
animation.onProgressUpdate?(progress)

Expand Down
97 changes: 91 additions & 6 deletions Source/animation/Easing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,95 @@
//
//

public enum Easing {
case ease
case linear
case easeIn
case easeOut
case easeInOut
#if os(iOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif

open class Easing {

public static let ease: Easing = Ease()
public static let linear: Easing = Easing()
public static let easeIn: Easing = EaseIn()
public static let easeOut: Easing = EaseOut()
public static let easeInOut: Easing = EaseInOut()
public static let elasticInOut: Easing = ElasticInOut()
public static func elasticInOut(elasticity: Double = 10.0) -> ElasticInOut {
return ElasticInOut(elasticity: elasticity)
}

open func caTimingFunction() -> CAMediaTimingFunction {
return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
}

open func progressForTimingFunction(progress: Double) -> Double {
return progress
}
}

private class Ease: Easing {
override open func caTimingFunction() -> CAMediaTimingFunction {
return CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
}
}

private class EaseIn: Easing {
override open func caTimingFunction() -> CAMediaTimingFunction {
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
}
override open func progressForTimingFunction(progress t: Double) -> Double {
return t * t
}
}

private class EaseOut: Easing {
override open func caTimingFunction() -> CAMediaTimingFunction {
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
}
override open func progressForTimingFunction(progress t: Double) -> Double {
return -(t * (t - 2))
}
}

private class EaseInOut: Easing {
override open func caTimingFunction() -> CAMediaTimingFunction {
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
}
override open func progressForTimingFunction(progress t: Double) -> Double {
if t < 0.5 {
return 2.0 * t * t
} else {
return -2.0 * t * t + 4.0 * t - 1.0
}
}
}

public class ElasticInOut: Easing {
let elasticity: Double

init(elasticity: Double = 10.0) { // less elasticity means more springy effect
self.elasticity = elasticity
}

override open func progressForTimingFunction(progress: Double) -> Double {
if progress == 0 {
return 0
}
var t = progress / 0.5
if t == 2 {
return 1
}
let p = 0.3 * 1.5
let s = p / 4

if t < 1 {
t -= 1
let postFix = pow(2, elasticity * t)
return (-0.5 * (postFix * sin((t - s) * (2 * .pi) / p)))
}
t -= 1
let postFix = pow(2, -elasticity * t)
return (postFix * sin((t - s) * (2 * .pi) / p ) * 0.5 + 1)
}
}
10 changes: 10 additions & 0 deletions Source/animation/types/ContentsAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public extension AnimatableVariable where T: ContentsInterpolation {
return ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: false)
}

public func animation(during: Double = 1.0, delay: Double = 0.0, _ f: @escaping ((Double) -> [Node])) -> Animation {
let group = node! as! Group
return ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: false)
}

public func animate(_ f: @escaping (Double) -> [Node]) {
let group = node! as! Group
_ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: 1.0, delay: 0.0, autostart: true)
Expand All @@ -60,4 +65,9 @@ public extension AnimatableVariable where T: ContentsInterpolation {
let group = node! as! Group
_ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: true)
}

public func animate(during: Double = 1.0, delay: Double = 0.0, _ f: @escaping ((Double) -> [Node])) {
let group = node! as! Group
_ = ContentsAnimation(animatedGroup: group, valueFunc: f, animationDuration: during, delay: delay, autostart: true)
}
}
26 changes: 26 additions & 0 deletions Source/animation/types/TransformAnimation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Foundation

internal class TransformAnimation: AnimationImpl<Transform> {

var trajectory: Path?

convenience init(animatedNode: Node, startValue: Transform, finalValue: Transform, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {

let interpolationFunc = { (t: Double) -> Transform in
Expand Down Expand Up @@ -31,6 +33,17 @@ internal class TransformAnimation: AnimationImpl<Transform> {
}
}

init(animatedNode: Node, factory: @escaping (() -> ((Double) -> Transform)), along path: Path, animationDuration: Double = 1.0, delay: Double = 0.0, autostart: Bool = false) {
super.init(observableValue: animatedNode.placeVar, factory: factory, animationDuration: animationDuration, delay: delay)
type = .affineTransformation
nodeId = animatedNode.id
self.trajectory = path

if autostart {
self.play()
}
}

open override func reverse() -> Animation {

let factory = { () -> (Double) -> Transform in
Expand Down Expand Up @@ -70,6 +83,11 @@ public extension AnimatableVariable where T: TransformInterpolation {
animation.play()
}

public func animate(along path: Path, during: Double = 1.0, delay: Double = 0.0) {
let animation = self.animation(along: path, during: during, delay: delay)
animation.play()
}

public func animation(from: Transform? = nil, to: Transform, during: Double = 1.0, delay: Double = 0.0) -> Animation {
if let safeFrom = from {
return self.animation((safeFrom >> to).t(during, delay: delay))
Expand Down Expand Up @@ -115,4 +133,12 @@ public extension AnimatableVariable where T: TransformInterpolation {
return TransformAnimation(animatedNode: self.node!, factory: factory, animationDuration: during, delay: delay)
}

public func animation(along path: Path, during: Double = 1.0, delay: Double = 0.0) -> Animation {

let factory = { () -> (Double) -> Transform in
return { (t: Double) in return Transform.identity }
}
return TransformAnimation(animatedNode: self.node!, factory: factory, along: path, animationDuration: during, delay: delay)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anim
renderTransform: layer.renderTransform!)

generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.timingFunction = animation.easing.caTimingFunction()
generatedAnim.autoreverses = animation.autoreverses

generatedAnim.completion = { finished in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func addOpacityAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, anima
offset: animation.pausedProgress,
fps: opacityAnimation.logicalFps)
generatedAnimation.repeatCount = Float(animation.repeatCount)
generatedAnimation.timingFunction = caTimingFunction(animation.easing)
generatedAnimation.timingFunction = animation.easing.caTimingFunction()

generatedAnimation.completion = { finished in

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func addShapeAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, animati
renderTransform: layer.renderTransform!)

generatedAnim.repeatCount = Float(animation.repeatCount)
generatedAnim.timingFunction = caTimingFunction(animation.easing)
generatedAnim.timingFunction = animation.easing.caTimingFunction()
generatedAnim.autoreverses = animation.autoreverses

generatedAnim.completion = { finished in
Expand Down
43 changes: 0 additions & 43 deletions Source/animation/types/animation_generators/TimingFunction.swift

This file was deleted.

Loading

0 comments on commit 24c8e0b

Please sign in to comment.