mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Various improvements
This commit is contained in:
204
submodules/PremiumUI/Sources/SwirlStarsView.swift
Normal file
204
submodules/PremiumUI/Sources/SwirlStarsView.swift
Normal file
@@ -0,0 +1,204 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SceneKit
|
||||
import Display
|
||||
import AppBundle
|
||||
import SwiftSignalKit
|
||||
|
||||
final class SwirlStarsView: UIView, PhoneDemoDecorationView {
|
||||
private let sceneView: SCNView
|
||||
|
||||
private var particles: SCNNode?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.sceneView = SCNView(frame: CGRect(origin: .zero, size: frame.size))
|
||||
self.sceneView.backgroundColor = .clear
|
||||
if let url = getAppBundle().url(forResource: "swirl", withExtension: "scn") {
|
||||
self.sceneView.scene = try? SCNScene(url: url, options: nil)
|
||||
}
|
||||
self.sceneView.isUserInteractionEnabled = false
|
||||
self.sceneView.preferredFramesPerSecond = 60
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.alpha = 0.0
|
||||
|
||||
self.addSubview(self.sceneView)
|
||||
|
||||
self.particles = self.sceneView.scene?.rootNode.childNode(withName: "particles", recursively: false)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.particles = nil
|
||||
}
|
||||
|
||||
func setVisible(_ visible: Bool) {
|
||||
if visible, let particles = self.particles, particles.parent == nil {
|
||||
self.sceneView.scene?.rootNode.addChildNode(particles)
|
||||
}
|
||||
self.setupAnimations()
|
||||
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear)
|
||||
transition.updateAlpha(layer: self.layer, alpha: visible ? 0.6 : 0.0, completion: { [weak self] finished in
|
||||
if let strongSelf = self, finished && !visible && strongSelf.particles?.parent != nil {
|
||||
strongSelf.particles?.removeFromParentNode()
|
||||
|
||||
if let node = strongSelf.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false) {
|
||||
node.removeAllAnimations()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setupAnimations() {
|
||||
guard let node = self.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false), node.animationKeys.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let initial = node.eulerAngles
|
||||
let target = SCNVector3(x: node.eulerAngles.x + .pi * 2.0, y: node.eulerAngles.y, z: node.eulerAngles.z)
|
||||
|
||||
let animation = CABasicAnimation(keyPath: "eulerAngles")
|
||||
animation.fromValue = NSValue(scnVector3: initial)
|
||||
animation.toValue = NSValue(scnVector3: target)
|
||||
animation.duration = 1.5
|
||||
animation.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
animation.fillMode = .forwards
|
||||
animation.repeatCount = .infinity
|
||||
node.addAnimation(animation, forKey: "rotation")
|
||||
|
||||
self.setupMovementAnimation()
|
||||
}
|
||||
|
||||
func setupMovementAnimation() {
|
||||
guard let node = self.sceneView.scene?.rootNode.childNode(withName: "star", recursively: false) else {
|
||||
return
|
||||
}
|
||||
|
||||
node.position = SCNVector3(3.5, 0.0, -2.0)
|
||||
let firstPath = UIBezierPath()
|
||||
firstPath.move(to: CGPoint(x: 3.5, y: -2.0))
|
||||
firstPath.addLine(to: CGPoint(x: -15.5, y: 15.5))
|
||||
|
||||
let firstAction = SCNAction.moveAlong(path: firstPath, duration: 2.0)
|
||||
|
||||
SCNTransaction.begin()
|
||||
SCNTransaction.animationDuration = 2.0
|
||||
node.runAction(firstAction)
|
||||
SCNTransaction.completionBlock = { [weak self, weak node] in
|
||||
Queue.mainQueue().after(2.2, {
|
||||
node?.position = SCNVector3(0.0, 0.0, -3.0)
|
||||
let secondPath = UIBezierPath()
|
||||
secondPath.move(to: CGPoint(x: 0.0, y: -3.0))
|
||||
secondPath.addLine(to: CGPoint(x: 15.5, y: 20.0))
|
||||
|
||||
let secondAction = SCNAction.moveAlong(path: secondPath, duration: 2.0)
|
||||
SCNTransaction.begin()
|
||||
SCNTransaction.animationDuration = 2.0
|
||||
node?.runAction(secondAction)
|
||||
SCNTransaction.completionBlock = { [weak self] in
|
||||
Queue.mainQueue().after(2.2, {
|
||||
self?.setupMovementAnimation()
|
||||
})
|
||||
}
|
||||
SCNTransaction.commit()
|
||||
})
|
||||
}
|
||||
SCNTransaction.commit()
|
||||
}
|
||||
|
||||
func resetAnimation() {
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.sceneView.frame = CGRect(origin: .zero, size: frame.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIBezierPath {
|
||||
var elements: [PathElement] {
|
||||
var pathElements = [PathElement]()
|
||||
withUnsafeMutablePointer(to: &pathElements) { elementsPointer in
|
||||
cgPath.apply(info: elementsPointer) { (userInfo, nextElementPointer) in
|
||||
let nextElement = PathElement(element: nextElementPointer.pointee)
|
||||
let elementsPointer = userInfo!.assumingMemoryBound(to: [PathElement].self)
|
||||
elementsPointer.pointee.append(nextElement)
|
||||
}
|
||||
}
|
||||
return pathElements
|
||||
}
|
||||
}
|
||||
|
||||
enum PathElement {
|
||||
case moveToPoint(CGPoint)
|
||||
case addLineToPoint(CGPoint)
|
||||
case addQuadCurveToPoint(CGPoint, CGPoint)
|
||||
case addCurveToPoint(CGPoint, CGPoint, CGPoint)
|
||||
case closeSubpath
|
||||
|
||||
init(element: CGPathElement) {
|
||||
switch element.type {
|
||||
case .moveToPoint:
|
||||
self = .moveToPoint(element.points[0])
|
||||
case .addLineToPoint:
|
||||
self = .addLineToPoint(element.points[0])
|
||||
case .addQuadCurveToPoint:
|
||||
self = .addQuadCurveToPoint(element.points[0], element.points[1])
|
||||
case .addCurveToPoint:
|
||||
self = .addCurveToPoint(element.points[0], element.points[1], element.points[2])
|
||||
case .closeSubpath:
|
||||
self = .closeSubpath
|
||||
@unknown default:
|
||||
self = .closeSubpath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension SCNAction {
|
||||
class func moveAlong(path: UIBezierPath, duration animationDuration: Double) -> SCNAction {
|
||||
let points = path.elements
|
||||
var actions = [SCNAction]()
|
||||
|
||||
for point in points {
|
||||
switch point {
|
||||
case .moveToPoint(let a):
|
||||
let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration)
|
||||
actions.append(moveAction)
|
||||
break
|
||||
|
||||
case .addCurveToPoint(let a, let b, let c):
|
||||
let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration)
|
||||
let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration)
|
||||
let moveAction3 = SCNAction.move(to: SCNVector3(c.x, 0, c.y), duration: animationDuration)
|
||||
actions.append(moveAction1)
|
||||
actions.append(moveAction2)
|
||||
actions.append(moveAction3)
|
||||
break
|
||||
|
||||
case .addLineToPoint(let a):
|
||||
let moveAction = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration)
|
||||
actions.append(moveAction)
|
||||
break
|
||||
|
||||
case .addQuadCurveToPoint(let a, let b):
|
||||
let moveAction1 = SCNAction.move(to: SCNVector3(a.x, 0, a.y), duration: animationDuration)
|
||||
let moveAction2 = SCNAction.move(to: SCNVector3(b.x, 0, b.y), duration: animationDuration)
|
||||
actions.append(moveAction1)
|
||||
actions.append(moveAction2)
|
||||
break
|
||||
|
||||
default:
|
||||
let moveAction = SCNAction.move(to: SCNVector3(0, 0, 0), duration: animationDuration)
|
||||
actions.append(moveAction)
|
||||
break
|
||||
}
|
||||
}
|
||||
return SCNAction.sequence(actions)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user