Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce LinearGradient animation #729

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import AppKit

class AnimationUtils {

class func layerForNodeRenderer(_ renderer: NodeRenderer, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true) -> ShapeLayer {
class func layerForNodeRenderer(_ renderer: NodeRenderer, animation: Animation, customBounds: Rect? = .none, shouldRenderContent: Bool = true, isGradient: Bool = false) -> LayerProtocol {

let node = renderer.node
if let cachedLayer = renderer.layer {
Expand All @@ -18,10 +18,10 @@ class AnimationUtils {
}

// 'sublayer' is for actual CAAnimations, and 'layer' is for manual transforming and hierarchy changes
let sublayer = ShapeLayer()
let sublayer: LayerProtocol = isGradient ? GradientLayer () : ShapeLayer()
sublayer.shouldRenderContent = shouldRenderContent

let layer = ShapeLayer()
let layer: LayerProtocol = isGradient ? GradientLayer () : ShapeLayer()
layer.addSublayer(sublayer)
layer.masksToBounds = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,15 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte

completion()
}

layer.path = fromLocus.toCGPath()
layer.setupStrokeAndFill(shape)

if let layer = layer as? ShapeLayer {
layer.path = fromLocus.toCGPath()
layer.setupStrokeAndFill(shape)
} else if let layer = layer as? GradientLayer {
let maskLayer = CAShapeLayer()
maskLayer.path = fromLocus.toCGPath()
layer.mask = maskLayer
}

let animationId = animation.ID
layer.add(generatedAnimation, forKey: animationId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func addPathAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
return
}

let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false, isGradient: shape.fill is LinearGradient)

// Creating proper animation
let generatedAnim = generatePathAnimation(
Expand Down Expand Up @@ -52,9 +52,11 @@ func addPathAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
completion()
}

//layer.path = RenderUtils.toCGPath(shape.form).copy(using: &layer.transform)
layer.path = shape.form.toCGPath()
layer.setupStrokeAndFill(shape)
if let layer = layer as? ShapeLayer {
//layer.path = RenderUtils.toCGPath(shape.form).copy(using: &layer.transform)
layer.path = shape.form.toCGPath()
layer.setupStrokeAndFill(shape)
}

layer.add(generatedAnim, forKey: animation.ID)
animation.removeFunc = { [weak layer] in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
let toShape = shapeAnimation.getVFunc()(animation.autoreverses ? 0.5 : 1.0)
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()

let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false, isGradient: shape.fill is LinearGradient)

// Creating proper animation
let generatedAnimation = generateShapeAnimation(context,
Expand Down Expand Up @@ -89,8 +89,22 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
completion()
}

layer.path = fromShape.form.toCGPath()
layer.setupStrokeAndFill(fromShape)
if let layer = layer as? ShapeLayer {
layer.path = fromShape.form.toCGPath()
layer.setupStrokeAndFill(fromShape)
} else if let layer = layer as? GradientLayer {
let maskLayer = CAShapeLayer()
maskLayer.path = fromShape.form.toCGPath()
layer.mask = maskLayer

if let color = shape.fill as? LinearGradient {
let properties = color.toCG()
layer.colors = properties.colors
layer.locations = properties.locations
layer.startPoint = CGPoint(x: color.x1, y: color.y1)
layer.endPoint = CGPoint(x: color.x2, y: color.y2)
}
}

let animationId = animation.ID
layer.add(generatedAnimation, forKey: animationId)
Expand Down Expand Up @@ -127,16 +141,25 @@ fileprivate func generateShapeAnimation(_ context: AnimationContext, from: Shape
group.animations?.append(transformAnimation)

// Fill
let fromFillColor = from.fill as? Color ?? Color.clear
let toFillColor = to.fill as? Color ?? Color.clear

if fromFillColor != toFillColor {
let fillAnimation = CABasicAnimation(keyPath: "fillColor")
fillAnimation.fromValue = fromFillColor.toCG()
fillAnimation.toValue = toFillColor.toCG()
fillAnimation.duration = duration

group.animations?.append(fillAnimation)

if from.fill != to.fill {
if let fromFillColor = from.fill as? Color, let toFillColor = to.fill as? Color {
let fillAnimation = CABasicAnimation(keyPath: "fillColor")
fillAnimation.fromValue = fromFillColor.toCG()
fillAnimation.toValue = toFillColor.toCG()
fillAnimation.duration = duration

group.animations?.append(fillAnimation)
} else if let fromFillColor = from.fill as? LinearGradient, let toFillColor = to.fill as? LinearGradient {
let fillAnimation = CABasicAnimation(keyPath: "colors")
let fromProperties = fromFillColor.toCG()
let toProperties = toFillColor.toCG()
fillAnimation.fromValue = fromProperties.colors
fillAnimation.toValue = toProperties.colors
fillAnimation.duration = duration

group.animations?.append(fillAnimation)
}
}

// Stroke
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func addTransformAnimation(_ animation: BasicAnimation, _ context: AnimationCont
let transactionsDisabled = CATransaction.disableActions()
CATransaction.setDisableActions(true)

let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: true)
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: true, isGradient: (node as? Shape)?.fill is LinearGradient)

// Creating proper animation
let generatedAnimation = transformAnimationByFunc(transformAnimation,
Expand Down
13 changes: 13 additions & 0 deletions Source/model/draw/Gradient.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import UIKit

open class Gradient: Fill {

public let userSpace: Bool
Expand All @@ -19,4 +21,15 @@ open class Gradient: Fill {

return stops.elementsEqual(other.stops)
}

public func toCG() -> (colors: Array<CGColor>, locations: Array<NSNumber>) {
var colors: Array<CGColor> = []
var locations: Array<NSNumber> = []
for stop in stops {
colors += [stop.color.toCG()]
locations += [NSNumber(value: stop.offset)]
}

return (colors, locations)
}
}
6 changes: 3 additions & 3 deletions Source/render/NodeRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ enum ColoringMode {
}

class CachedLayer {
let rootLayer: ShapeLayer
let animationLayer: ShapeLayer
let rootLayer: LayerProtocol
let animationLayer: LayerProtocol

required init(rootLayer: ShapeLayer, animationLayer: ShapeLayer) {
required init(rootLayer: LayerProtocol, animationLayer: LayerProtocol) {
self.rootLayer = rootLayer
self.animationLayer = animationLayer
}
Expand Down
28 changes: 25 additions & 3 deletions Source/views/ShapeLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,32 @@ import UIKit
import AppKit
#endif

class ShapeLayer: CAShapeLayer {
protocol LayerProtocol: CALayer {
var renderer: NodeRenderer? { get set }
var shouldRenderContent: Bool { get set }
var isForceRenderingEnabled: Bool { get set }
func draw(in ctx: CGContext)
}

class ShapeLayer: CAShapeLayer, LayerProtocol {
weak var renderer: NodeRenderer?
var shouldRenderContent: Bool = true
var isForceRenderingEnabled: Bool = true

override func draw(in ctx: CGContext) {
if !shouldRenderContent {
super.draw(in: ctx)
return
}

renderer?.directRender(in: ctx, force: isForceRenderingEnabled)
}
}

class GradientLayer: CAGradientLayer, LayerProtocol {
weak var renderer: NodeRenderer?
var shouldRenderContent = true
var isForceRenderingEnabled = true
var shouldRenderContent: Bool = true
var isForceRenderingEnabled: Bool = true

override func draw(in ctx: CGContext) {
if !shouldRenderContent {
Expand Down