diff --git a/Example/Example/Examples/PathAnimation/PathAnimationView.swift b/Example/Example/Examples/PathAnimation/PathAnimationView.swift index 9314e005..d146a685 100644 --- a/Example/Example/Examples/PathAnimation/PathAnimationView.swift +++ b/Example/Example/Examples/PathAnimation/PathAnimationView.swift @@ -30,7 +30,7 @@ class PathAnimationView: MacawView { func fractalStep(allTriangles: [Shape], currentTier: [Shape], side: Double, depth: Int) { var tierAnimations = [Animation]() for shape in currentTier { - tierAnimations.append(shape.strokeEndVar.animation(to: StrokeEnd(1))) + tierAnimations.append(shape.strokeVar.end.animation(to: 1)) } tierAnimations.combine().onComplete { if depth < 4 { diff --git a/Source/animation/layer_animation/Extensions/AnimOperators.swift b/Source/animation/layer_animation/Extensions/AnimOperators.swift index 2e4e568e..202cb07e 100644 --- a/Source/animation/layer_animation/Extensions/AnimOperators.swift +++ b/Source/animation/layer_animation/Extensions/AnimOperators.swift @@ -7,12 +7,6 @@ public func >> (a: Double, b: Double) -> OpacityAnimationDescription { }) } -public func >> (a: StrokeEnd, b: StrokeEnd) -> PathAnimationDescription { - return PathAnimationDescription(valueFunc: { t in - a.interpolate(b, progress: t) - }) -} - public func >> (a: Transform, b: Transform) -> TransformAnimationDescription { return TransformAnimationDescription(valueFunc: { t in a.interpolate(b, progress: t) diff --git a/Source/animation/layer_animation/Extensions/DoubleInterpolation.swift b/Source/animation/layer_animation/Extensions/DoubleInterpolation.swift index 6e4e7685..78b287fa 100644 --- a/Source/animation/layer_animation/Extensions/DoubleInterpolation.swift +++ b/Source/animation/layer_animation/Extensions/DoubleInterpolation.swift @@ -7,25 +7,3 @@ extension Double: DoubleInterpolation { return self + (endValue - self) * progress } } - -public final class StrokeEnd { - var double: Double = 0 - - public init(_ double: Double) { - self.double = double - } - - public static var zero: StrokeEnd { - return StrokeEnd(0) - } -} - -public protocol StrokeEndInterpolation: Interpolable { - -} - -extension StrokeEnd: StrokeEndInterpolation { - public func interpolate(_ endValue: StrokeEnd, progress: Double) -> StrokeEnd { - return StrokeEnd(self.double + (endValue.double - self.double) * progress) - } -} diff --git a/Source/animation/types/PathAnimation.swift b/Source/animation/types/PathAnimation.swift index fdfb12a1..0420d7e0 100644 --- a/Source/animation/types/PathAnimation.swift +++ b/Source/animation/types/PathAnimation.swift @@ -7,19 +7,21 @@ import Foundation -class PathAnimation: AnimationImpl { +class PathAnimation: AnimationImpl { - convenience init(animatedNode: Shape, startValue: StrokeEnd, finalValue: StrokeEnd, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { + let isEnd: Bool = true - let interpolationFunc = { (t: Double) -> StrokeEnd in + convenience init(animatedNode: Shape, isEnd: Bool, startValue: Double, finalValue: Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { + + let interpolationFunc = { (t: Double) -> Double in startValue.interpolate(finalValue, progress: t) } - self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps) + self.init(animatedNode: animatedNode, isEnd: isEnd, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps) } - init(animatedNode: Shape, valueFunc: @escaping (Double) -> StrokeEnd, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { - super.init(observableValue: animatedNode.strokeEndVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps) + init(animatedNode: Shape, isEnd: Bool, valueFunc: @escaping (Double) -> Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { + super.init(observableValue: AnimatableVariable(isEnd ? animatedNode.strokeVar.end.value : animatedNode.strokeVar.start.value), valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps) type = .path node = animatedNode @@ -28,8 +30,8 @@ class PathAnimation: AnimationImpl { } } - init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> StrokeEnd)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { - super.init(observableValue: animatedNode.strokeEndVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps) + init(animatedNode: Shape, isEnd: Bool, factory: @escaping (() -> ((Double) -> Double)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) { + super.init(observableValue: AnimatableVariable(isEnd ? animatedNode.strokeVar.end.value : animatedNode.strokeVar.start.value), factory: factory, animationDuration: animationDuration, delay: delay, fps: fps) type = .path node = animatedNode @@ -44,34 +46,59 @@ class PathAnimation: AnimationImpl { } } -public typealias PathAnimationDescription = AnimationDescription +public typealias PathAnimationDescription = AnimationDescription -public extension AnimatableVariable where T: StrokeEndInterpolation { +open class StrokeAnimatableVariable: AnimatableVariable { - func animate(_ desc: PathAnimationDescription) { - _ = PathAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true) + public var end: StrokeSideVariable { + return StrokeSideVariable(parentVar: self, isEnd: true) } - func animation(_ desc: PathAnimationDescription) -> Animation { - return PathAnimation(animatedNode: node as! Shape, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false) + public var start: StrokeSideVariable { + return StrokeSideVariable(parentVar: self, isEnd: false) + } +} + +open class StrokeSideVariable { + + let parentVar: StrokeAnimatableVariable + let isEnd: Bool + var value: Double = 0 + + var node: Node? { + parentVar.node } - func animate(from: StrokeEnd? = nil, to: StrokeEnd, during: Double = 1.0, delay: Double = 0.0) { - self.animate(((from ?? StrokeEnd.zero) >> to).t(during, delay: delay)) + init(parentVar: StrokeAnimatableVariable, isEnd: Bool, value: Double = 0) { + self.parentVar = parentVar + self.isEnd = isEnd + self.value = value } - func animation(from: StrokeEnd? = nil, to: StrokeEnd, during: Double = 1.0, delay: Double = 0.0) -> Animation { + public func animate(_ desc: PathAnimationDescription) { + _ = PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true) + } + + public func animation(_ desc: PathAnimationDescription) -> Animation { + return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false) + } + + public func animate(from: Double? = nil, to: Double = 1, during: Double = 1.0, delay: Double = 0.0) { + self.animate(((from ?? 0) >> to).t(during, delay: delay)) + } + + public func animation(from: Double? = nil, to: Double = 1, during: Double = 1.0, delay: Double = 0.0) -> Animation { if let safeFrom = from { return self.animation((safeFrom >> to).t(during, delay: delay)) } - let origin = StrokeEnd.zero - let factory = { () -> (Double) -> StrokeEnd in + let origin = Double(0) + let factory = { () -> (Double) -> Double in { (t: Double) in origin.interpolate(to, progress: t) } } - return PathAnimation(animatedNode: node as! Shape, factory: factory, animationDuration: during, delay: delay) + return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, factory: factory, animationDuration: during, delay: delay) } - func animation(_ f: @escaping ((Double) -> StrokeEnd), during: Double = 1.0, delay: Double = 0.0) -> Animation { - return PathAnimation(animatedNode: node as! Shape, valueFunc: f, animationDuration: during, delay: delay) + public func animation(_ f: @escaping ((Double) -> Double), during: Double = 1.0, delay: Double = 0.0) -> Animation { + return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: f, animationDuration: during, delay: delay) } } diff --git a/Source/animation/types/animation_generators/PathAnimationGenerator.swift b/Source/animation/types/animation_generators/PathAnimationGenerator.swift index d5575d10..c7bd052e 100644 --- a/Source/animation/types/animation_generators/PathAnimationGenerator.swift +++ b/Source/animation/types/animation_generators/PathAnimationGenerator.swift @@ -62,7 +62,7 @@ func addPathAnimation(_ animation: BasicAnimation, _ context: AnimationContext, } } -fileprivate func generatePathAnimation(_ valueFunc: (Double) -> StrokeEnd, duration: Double, offset: Double, fps: UInt) -> CAAnimation { +fileprivate func generatePathAnimation(_ valueFunc: (Double) -> Double, duration: Double, offset: Double, fps: UInt) -> CAAnimation { var strokeEndValues = [Double]() var timeValues = [Double]() @@ -80,7 +80,7 @@ fileprivate func generatePathAnimation(_ valueFunc: (Double) -> StrokeEnd, durat } let value = valueFunc(offset + dt) - strokeEndValues.append(value.double) + strokeEndValues.append(value) timeValues.append(dt) } diff --git a/Source/model/scene/Shape.swift b/Source/model/scene/Shape.swift index 67dc6eb8..331bf5d7 100644 --- a/Source/model/scene/Shape.swift +++ b/Source/model/scene/Shape.swift @@ -18,19 +18,16 @@ open class Shape: Node { set(val) { fillVar.value = val } } - public let strokeVar: AnimatableVariable + public let strokeVar: StrokeAnimatableVariable open var stroke: Stroke? { get { return strokeVar.value } set(val) { strokeVar.value = val } } - public let strokeEndVar: AnimatableVariable - public init(form: Locus, fill: Fill? = nil, stroke: Stroke? = nil, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) { self.formVar = AnimatableVariable(form) self.fillVar = AnimatableVariable(fill) - self.strokeVar = AnimatableVariable(stroke) - self.strokeEndVar = AnimatableVariable(StrokeEnd.zero) + self.strokeVar = StrokeAnimatableVariable(stroke) super.init( place: place, opaque: opaque, @@ -43,9 +40,8 @@ open class Shape: Node { ) self.formVar.node = self - self.strokeVar.node = self self.fillVar.node = self - self.strokeEndVar.node = self + self.strokeVar.node = self } override open var bounds: Rect? {