1
1
mirror of https://github.com/exyte/Macaw.git synced 2024-08-16 00:20:23 +03:00

introduce LinearGradient animation

This commit is contained in:
iBlacksus 2020-08-30 01:00:23 +03:00
parent bdc3b499c1
commit f8fc7d3168
8 changed files with 94 additions and 28 deletions

View File

@ -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 {
@ -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

View File

@ -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)

View File

@ -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(
@ -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

View File

@ -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,
@ -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)
@ -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 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
if fromFillColor != toFillColor {
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)
group.animations?.append(fillAnimation)
}
}
// Stroke

View File

@ -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,

View File

@ -1,3 +1,5 @@
import UIKit
open class Gradient: Fill {
public let userSpace: Bool
@ -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)
}
}

View File

@ -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
}

View File

@ -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 = true
var isForceRenderingEnabled = true
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: Bool = true
var isForceRenderingEnabled: Bool = true
override func draw(in ctx: CGContext) {
if !shouldRenderContent {