mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
140 lines
3.6 KiB
Swift
140 lines
3.6 KiB
Swift
//
|
|
// EllipseNode.swift
|
|
// lottie-swift
|
|
//
|
|
// Created by Brandon Withrow on 1/17/19.
|
|
//
|
|
|
|
import Foundation
|
|
import QuartzCore
|
|
|
|
// MARK: - EllipseNodeProperties
|
|
|
|
final class EllipseNodeProperties: NodePropertyMap, KeypathSearchable {
|
|
|
|
// MARK: Lifecycle
|
|
|
|
init(ellipse: Ellipse) {
|
|
keypathName = ellipse.name
|
|
direction = ellipse.direction
|
|
position = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.position.keyframes))
|
|
size = NodeProperty(provider: KeyframeInterpolator(keyframes: ellipse.size.keyframes))
|
|
keypathProperties = [
|
|
"Position" : position,
|
|
"Size" : size,
|
|
]
|
|
properties = Array(keypathProperties.values)
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
var keypathName: String
|
|
|
|
let direction: PathDirection
|
|
let position: NodeProperty<Vector3D>
|
|
let size: NodeProperty<Vector3D>
|
|
|
|
let keypathProperties: [String: AnyNodeProperty]
|
|
let properties: [AnyNodeProperty]
|
|
}
|
|
|
|
// MARK: - EllipseNode
|
|
|
|
final class EllipseNode: AnimatorNode, PathNode {
|
|
|
|
// MARK: Lifecycle
|
|
|
|
init(parentNode: AnimatorNode?, ellipse: Ellipse) {
|
|
pathOutput = PathOutputNode(parent: parentNode?.outputNode)
|
|
properties = EllipseNodeProperties(ellipse: ellipse)
|
|
self.parentNode = parentNode
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
static let ControlPointConstant: CGFloat = 0.55228
|
|
|
|
let pathOutput: PathOutputNode
|
|
|
|
let properties: EllipseNodeProperties
|
|
|
|
let parentNode: AnimatorNode?
|
|
var hasLocalUpdates = false
|
|
var hasUpstreamUpdates = false
|
|
var lastUpdateFrame: CGFloat? = nil
|
|
|
|
// MARK: Animator Node
|
|
|
|
var propertyMap: NodePropertyMap & KeypathSearchable {
|
|
properties
|
|
}
|
|
|
|
var isEnabled = true {
|
|
didSet {
|
|
pathOutput.isEnabled = isEnabled
|
|
}
|
|
}
|
|
|
|
func rebuildOutputs(frame: CGFloat) {
|
|
pathOutput.setPath(
|
|
.ellipse(
|
|
size: properties.size.value.sizeValue,
|
|
center: properties.position.value.pointValue,
|
|
direction: properties.direction),
|
|
updateFrame: frame)
|
|
}
|
|
|
|
}
|
|
|
|
extension BezierPath {
|
|
/// Constructs a `BezierPath` in the shape of an ellipse
|
|
static func ellipse(
|
|
size: CGSize,
|
|
center: CGPoint,
|
|
direction: PathDirection)
|
|
-> BezierPath
|
|
{
|
|
// Unfortunately we HAVE to manually build out the ellipse.
|
|
// Every Apple method constructs an ellipse from the 3 o-clock position
|
|
// After effects constructs from the Noon position.
|
|
// After effects does clockwise, but also has a flag for reversed.
|
|
var half = size * 0.5
|
|
if direction == .counterClockwise {
|
|
half.width = half.width * -1
|
|
}
|
|
|
|
let q1 = CGPoint(x: center.x, y: center.y - half.height)
|
|
let q2 = CGPoint(x: center.x + half.width, y: center.y)
|
|
let q3 = CGPoint(x: center.x, y: center.y + half.height)
|
|
let q4 = CGPoint(x: center.x - half.width, y: center.y)
|
|
|
|
let cp = half * EllipseNode.ControlPointConstant
|
|
|
|
var path = BezierPath(startPoint: CurveVertex(
|
|
point: q1,
|
|
inTangentRelative: CGPoint(x: -cp.width, y: 0),
|
|
outTangentRelative: CGPoint(x: cp.width, y: 0)))
|
|
path.addVertex(CurveVertex(
|
|
point: q2,
|
|
inTangentRelative: CGPoint(x: 0, y: -cp.height),
|
|
outTangentRelative: CGPoint(x: 0, y: cp.height)))
|
|
|
|
path.addVertex(CurveVertex(
|
|
point: q3,
|
|
inTangentRelative: CGPoint(x: cp.width, y: 0),
|
|
outTangentRelative: CGPoint(x: -cp.width, y: 0)))
|
|
|
|
path.addVertex(CurveVertex(
|
|
point: q4,
|
|
inTangentRelative: CGPoint(x: 0, y: cp.height),
|
|
outTangentRelative: CGPoint(x: 0, y: -cp.height)))
|
|
|
|
path.addVertex(CurveVertex(
|
|
point: q1,
|
|
inTangentRelative: CGPoint(x: -cp.width, y: 0),
|
|
outTangentRelative: CGPoint(x: cp.width, y: 0)))
|
|
path.close()
|
|
return path
|
|
}
|
|
}
|