mirror of
https://github.com/exyte/Macaw.git
synced 2024-09-11 13:15:35 +03:00
Merge pull request #463 from f3dm76/task/elastic
Add spring to transform animation
This commit is contained in:
commit
dd1ad89f53
@ -14,11 +14,11 @@ class EasingView: MacawView {
|
||||
let fromStroke = Stroke(fill: Color.black, width: 3)
|
||||
let toStroke = Stroke(fill: Color.black, width: 1, dashes: [3, 3])
|
||||
|
||||
let all = [Easing.ease, Easing.linear, Easing.easeIn, Easing.easeOut, Easing.easeInOut]
|
||||
let all = [Easing.ease, Easing.linear, Easing.easeIn, Easing.easeOut, Easing.easeInOut, Easing.elasticInOut]
|
||||
|
||||
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)
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 */; };
|
||||
@ -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 */; };
|
||||
@ -450,6 +448,8 @@
|
||||
5B6E194220AC58F900454E7E /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6E191F20AC58F900454E7E /* Color.swift */; };
|
||||
5B6E194320AC58F900454E7E /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6E192020AC58F900454E7E /* Gradient.swift */; };
|
||||
5B6E194420AC58F900454E7E /* Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6E192020AC58F900454E7E /* Gradient.swift */; };
|
||||
5B7D7ED321300D4A00B5ED00 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */; };
|
||||
5B7D7ED421300D4A00B5ED00 /* TimingFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */; };
|
||||
5B7E79CE20CBE69700C50BCF /* masking-path-02-b-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CC20CBE69600C50BCF /* masking-path-02-b-manual.reference */; };
|
||||
5B7E79CF20CBE69700C50BCF /* masking-path-02-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */; };
|
||||
5B7E79DE20D2781A00C50BCF /* masking-intro-01-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */; };
|
||||
@ -581,7 +581,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>"; };
|
||||
@ -852,6 +851,7 @@
|
||||
5B6E191E20AC58F900454E7E /* Stroke.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stroke.swift; sourceTree = "<group>"; };
|
||||
5B6E191F20AC58F900454E7E /* Color.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
|
||||
5B6E192020AC58F900454E7E /* Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Gradient.swift; sourceTree = "<group>"; };
|
||||
5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingFunction.swift; sourceTree = "<group>"; };
|
||||
5B7E79CC20CBE69600C50BCF /* masking-path-02-b-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-02-b-manual.reference"; sourceTree = "<group>"; };
|
||||
5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-path-02-b-manual.svg"; sourceTree = "<group>"; };
|
||||
5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "masking-intro-01-f-manual.reference"; sourceTree = "<group>"; };
|
||||
@ -1091,7 +1091,7 @@
|
||||
57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */,
|
||||
57A27BD21E44C5570057BD3A /* ShapeAnimationGenerator.swift */,
|
||||
57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */,
|
||||
57E5E0FD1E3B393900D1CB28 /* TimingFunction.swift */,
|
||||
5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */,
|
||||
57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */,
|
||||
);
|
||||
path = animation_generators;
|
||||
@ -1970,7 +1970,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 */,
|
||||
@ -1982,6 +1981,7 @@
|
||||
57614B181F83D15600875933 /* SVGView.swift in Sources */,
|
||||
57614B191F83D15600875933 /* Arc.swift in Sources */,
|
||||
57614B1A1F83D15600875933 /* MacawView.swift in Sources */,
|
||||
5B7D7ED421300D4A00B5ED00 /* TimingFunction.swift in Sources */,
|
||||
5B6E193420AC58F900454E7E /* Baseline.swift in Sources */,
|
||||
57614B1B1F83D15600875933 /* Image.swift in Sources */,
|
||||
57614B1C1F83D15600875933 /* TransformGenerator.swift in Sources */,
|
||||
@ -2103,7 +2103,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 */,
|
||||
@ -2117,6 +2116,7 @@
|
||||
57E5E18B1E3B393900D1CB28 /* Arc.swift in Sources */,
|
||||
57E5E1B21E3B393900D1CB28 /* MacawView.swift in Sources */,
|
||||
57E5E19F1E3B393900D1CB28 /* Image.swift in Sources */,
|
||||
5B7D7ED321300D4A00B5ED00 /* TimingFunction.swift in Sources */,
|
||||
5B6E193320AC58F900454E7E /* Baseline.swift in Sources */,
|
||||
57E5E16A1E3B393900D1CB28 /* TransformGenerator.swift in Sources */,
|
||||
57E5E1551E3B393900D1CB28 /* AnimationImpl.swift in Sources */,
|
||||
|
@ -93,7 +93,6 @@ class AnimationProducer {
|
||||
self.addAnimation(next)
|
||||
}
|
||||
})
|
||||
|
||||
case .opacity:
|
||||
addOpacityAnimation(animation, sceneLayer: layer, animationCache: cache, completion: {
|
||||
if let next = animation.next {
|
||||
@ -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.progressFor(time: progress)
|
||||
unionBounds = unionBounds?.union(rect: contentsAnimation.getVFunc()(t).group().bounds!)
|
||||
}
|
||||
|
||||
guard let layer = cache?.layerForNode(node, animation: contentsAnimation, customBounds: unionBounds) else {
|
||||
@ -379,7 +378,7 @@ class AnimationProducer {
|
||||
continue
|
||||
}
|
||||
|
||||
let t = progressForTimingFunction(animation.easing, progress: progress)
|
||||
let t = animation.easing.progressFor(time: progress)
|
||||
group.contents = animation.getVFunc()(t)
|
||||
animation.onProgressUpdate?(progress)
|
||||
|
||||
|
@ -6,10 +6,76 @@
|
||||
//
|
||||
//
|
||||
|
||||
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 = Easing()
|
||||
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 progressFor(time: Double) -> Double {
|
||||
return time
|
||||
}
|
||||
}
|
||||
|
||||
private class EaseIn: Easing {
|
||||
override open func progressFor(time t: Double) -> Double {
|
||||
return t * t
|
||||
}
|
||||
}
|
||||
|
||||
private class EaseOut: Easing {
|
||||
override open func progressFor(time t: Double) -> Double {
|
||||
return -(t * (t - 2))
|
||||
}
|
||||
}
|
||||
|
||||
private class EaseInOut: Easing {
|
||||
override open func progressFor(time 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 progressFor(time: Double) -> Double {
|
||||
if time == 0 {
|
||||
return 0
|
||||
}
|
||||
var t = time / 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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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))
|
||||
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#elseif os(OSX)
|
||||
@ -7,37 +5,21 @@ import AppKit
|
||||
#endif
|
||||
|
||||
func caTimingFunction(_ easing: Easing) -> CAMediaTimingFunction {
|
||||
switch easing {
|
||||
case .ease:
|
||||
if easing === Easing.ease {
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
|
||||
case .linear:
|
||||
}
|
||||
if easing === Easing.linear {
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
|
||||
case .easeIn:
|
||||
}
|
||||
if easing === Easing.easeIn {
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
|
||||
case .easeOut:
|
||||
}
|
||||
if easing === Easing.easeOut {
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
|
||||
case .easeInOut:
|
||||
}
|
||||
if easing === Easing.easeInOut {
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
|
||||
}
|
||||
return CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
|
||||
}
|
||||
|
||||
func progressForTimingFunction(_ easing: Easing, progress: Double) -> Double {
|
||||
let t = progress
|
||||
|
||||
switch easing {
|
||||
case .ease:
|
||||
return t
|
||||
case .linear:
|
||||
return t
|
||||
case .easeIn:
|
||||
return t * t
|
||||
case .easeOut:
|
||||
return -(t * (t - 2))
|
||||
case .easeInOut:
|
||||
if t < 0.5 {
|
||||
return 2.0 * t * t
|
||||
} else {
|
||||
return -2.0 * t * t + 4.0 * t - 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,17 @@ func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, ani
|
||||
return
|
||||
}
|
||||
|
||||
if transformAnimation.trajectory != nil && transformAnimation.easing === Easing.elasticInOut {
|
||||
fatalError("Transform animation with trajectory can't have elastic easing, try using contentVar animation instead")
|
||||
}
|
||||
|
||||
node.placeVar.value = transformAnimation.getVFunc()(0.0)
|
||||
|
||||
// Creating proper animation
|
||||
var generatedAnimation: CAAnimation?
|
||||
|
||||
generatedAnimation = transformAnimationByFunc(node,
|
||||
valueFunc: transformAnimation.getVFunc(),
|
||||
generatedAnimation = transformAnimationByFunc(transformAnimation,
|
||||
node: node,
|
||||
duration: animation.getDuration(),
|
||||
offset: animation.pausedProgress,
|
||||
fps: transformAnimation.logicalFps)
|
||||
@ -29,7 +35,6 @@ func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, ani
|
||||
}
|
||||
|
||||
generatedAnim.repeatCount = Float(animation.repeatCount)
|
||||
generatedAnim.timingFunction = caTimingFunction(animation.easing)
|
||||
|
||||
generatedAnim.completion = { finished in
|
||||
|
||||
@ -75,36 +80,32 @@ func addTransformAnimation(_ animation: BasicAnimation, sceneLayer: CALayer, ani
|
||||
}
|
||||
}
|
||||
|
||||
func transfomToCG(_ transform: Transform) -> CGAffineTransform {
|
||||
return CGAffineTransform(
|
||||
a: CGFloat(transform.m11),
|
||||
b: CGFloat(transform.m12),
|
||||
c: CGFloat(transform.m21),
|
||||
d: CGFloat(transform.m22),
|
||||
tx: CGFloat(transform.dx),
|
||||
ty: CGFloat(transform.dy))
|
||||
}
|
||||
func transformAnimationByFunc(_ animation: TransformAnimation, node: Node, duration: Double, offset: Double, fps: UInt) -> CAAnimation {
|
||||
|
||||
func transformAnimationByFunc(_ node: Node, valueFunc: (Double) -> Transform, duration: Double, offset: Double, fps: UInt) -> CAAnimation {
|
||||
let valueFunc = animation.getVFunc()
|
||||
let view = nodesMap.getView(node)
|
||||
|
||||
if let trajectory = animation.trajectory {
|
||||
let pathAnimation = CAKeyframeAnimation(keyPath: "position")
|
||||
pathAnimation.timingFunction = caTimingFunction(animation.easing)
|
||||
pathAnimation.duration = duration / 2
|
||||
pathAnimation.autoreverses = animation.autoreverses
|
||||
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(0), view: view)
|
||||
pathAnimation.values = [NSValue(caTransform3D: CATransform3DMakeAffineTransform(value.toCG()))]
|
||||
pathAnimation.fillMode = kCAFillModeForwards
|
||||
pathAnimation.isRemovedOnCompletion = false
|
||||
pathAnimation.path = trajectory.toCGPath()
|
||||
|
||||
return pathAnimation
|
||||
}
|
||||
|
||||
var transformValues = [CATransform3D]()
|
||||
var timeValues = [Double]()
|
||||
|
||||
let step = 1.0 / (duration * Double(fps))
|
||||
var dt = 0.0
|
||||
var tValue = Array(stride(from: 0.0, to: 1.0, by: step))
|
||||
tValue.append(1.0)
|
||||
for t in tValue {
|
||||
|
||||
dt = t
|
||||
if 1.0 - dt < step {
|
||||
dt = 1.0
|
||||
}
|
||||
|
||||
timeValues.append(dt)
|
||||
|
||||
let view = nodesMap.getView(node)
|
||||
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(offset + dt), view: view)
|
||||
let progress = animation.easing.progressFor(time: t)
|
||||
let value = AnimationUtils.absoluteTransform(node, pos: valueFunc(offset + progress), view: view)
|
||||
let cgValue = CATransform3DMakeAffineTransform(value.toCG())
|
||||
transformValues.append(cgValue)
|
||||
}
|
||||
@ -112,13 +113,8 @@ func transformAnimationByFunc(_ node: Node, valueFunc: (Double) -> Transform, du
|
||||
let transformAnimation = CAKeyframeAnimation(keyPath: "transform")
|
||||
transformAnimation.duration = duration
|
||||
transformAnimation.values = transformValues
|
||||
transformAnimation.keyTimes = timeValues as [NSNumber]?
|
||||
transformAnimation.fillMode = kCAFillModeForwards
|
||||
transformAnimation.isRemovedOnCompletion = false
|
||||
|
||||
return transformAnimation
|
||||
}
|
||||
|
||||
func fixedAngle(_ angle: CGFloat) -> CGFloat {
|
||||
return angle > -0.0000000000000000000000001 ? angle : CGFloat(2.0 * Double.pi) + angle
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user