mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
201 lines
9.7 KiB
Swift
201 lines
9.7 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import AsyncDisplayKit
|
|
import Display
|
|
|
|
private final class VoiceChatMicrophoneNodeDrawingState: NSObject {
|
|
let color: UIColor
|
|
let transition: CGFloat
|
|
let reverse: Bool
|
|
|
|
init(color: UIColor, transition: CGFloat, reverse: Bool) {
|
|
self.color = color
|
|
self.transition = transition
|
|
self.reverse = reverse
|
|
|
|
super.init()
|
|
}
|
|
}
|
|
|
|
final class VoiceChatMicrophoneNode: ASDisplayNode {
|
|
class State: Equatable {
|
|
let muted: Bool
|
|
let color: UIColor
|
|
|
|
init(muted: Bool, color: UIColor) {
|
|
self.muted = muted
|
|
self.color = color
|
|
}
|
|
|
|
static func ==(lhs: State, rhs: State) -> Bool {
|
|
if lhs.muted != rhs.muted {
|
|
return false
|
|
}
|
|
if lhs.color.argb != rhs.color.argb {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
private class TransitionContext {
|
|
let startTime: Double
|
|
let duration: Double
|
|
let previousState: State
|
|
|
|
init(startTime: Double, duration: Double, previousState: State) {
|
|
self.startTime = startTime
|
|
self.duration = duration
|
|
self.previousState = previousState
|
|
}
|
|
}
|
|
|
|
private var animator: ConstantDisplayLinkAnimator?
|
|
|
|
private var hasState = false
|
|
private var state: State = State(muted: false, color: .black)
|
|
private var transitionContext: TransitionContext?
|
|
|
|
override init() {
|
|
super.init()
|
|
|
|
self.isOpaque = false
|
|
}
|
|
|
|
func update(state: State, animated: Bool) {
|
|
var animated = animated
|
|
if !self.hasState {
|
|
self.hasState = true
|
|
animated = false
|
|
}
|
|
|
|
if self.state != state {
|
|
let previousState = self.state
|
|
self.state = state
|
|
|
|
if animated {
|
|
self.transitionContext = TransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousState: previousState)
|
|
}
|
|
|
|
self.updateAnimations()
|
|
self.setNeedsDisplay()
|
|
}
|
|
}
|
|
|
|
private func updateAnimations() {
|
|
var animate = false
|
|
let timestamp = CACurrentMediaTime()
|
|
|
|
if let transitionContext = self.transitionContext {
|
|
if transitionContext.startTime + transitionContext.duration < timestamp {
|
|
self.transitionContext = nil
|
|
} else {
|
|
animate = true
|
|
}
|
|
}
|
|
|
|
if animate {
|
|
let animator: ConstantDisplayLinkAnimator
|
|
if let current = self.animator {
|
|
animator = current
|
|
} else {
|
|
animator = ConstantDisplayLinkAnimator(update: { [weak self] in
|
|
self?.updateAnimations()
|
|
})
|
|
self.animator = animator
|
|
}
|
|
animator.isPaused = false
|
|
} else {
|
|
self.animator?.isPaused = true
|
|
}
|
|
|
|
self.setNeedsDisplay()
|
|
}
|
|
|
|
override public func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
|
|
var transitionFraction: CGFloat = self.state.muted ? 1.0 : 0.0
|
|
var color = self.state.color
|
|
|
|
var reverse = false
|
|
if let transitionContext = self.transitionContext {
|
|
let timestamp = CACurrentMediaTime()
|
|
var t = CGFloat((timestamp - transitionContext.startTime) / transitionContext.duration)
|
|
t = min(1.0, max(0.0, t))
|
|
|
|
if transitionContext.previousState.muted != self.state.muted {
|
|
transitionFraction = self.state.muted ? t : 1.0 - t
|
|
|
|
reverse = transitionContext.previousState.muted
|
|
}
|
|
|
|
if transitionContext.previousState.color.rgb != color.rgb {
|
|
color = transitionContext.previousState.color.interpolateTo(color, fraction: t)!
|
|
}
|
|
}
|
|
|
|
return VoiceChatMicrophoneNodeDrawingState(color: color, transition: transitionFraction, reverse: reverse)
|
|
}
|
|
|
|
@objc override public class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
|
|
let context = UIGraphicsGetCurrentContext()!
|
|
|
|
if !isRasterizing {
|
|
context.setBlendMode(.copy)
|
|
context.setFillColor(UIColor.clear.cgColor)
|
|
context.fill(bounds)
|
|
}
|
|
|
|
guard let parameters = parameters as? VoiceChatMicrophoneNodeDrawingState else {
|
|
return
|
|
}
|
|
|
|
context.setFillColor(parameters.color.cgColor)
|
|
|
|
if bounds.size.width > 36.0 {
|
|
context.scaleBy(x: 2.5, y: 2.5)
|
|
}
|
|
context.translateBy(x: 18.0, y: 18.0)
|
|
|
|
let _ = try? drawSvgPath(context, path: "M-0.004000000189989805,-9.86400032043457 C2.2960000038146973,-9.86400032043457 4.165999889373779,-8.053999900817871 4.25600004196167,-5.77400016784668 C4.25600004196167,-5.77400016784668 4.265999794006348,-5.604000091552734 4.265999794006348,-5.604000091552734 C4.265999794006348,-5.604000091552734 4.265999794006348,-0.8040000200271606 4.265999794006348,-0.8040000200271606 C4.265999794006348,1.555999994277954 2.3559999465942383,3.4660000801086426 -0.004000000189989805,3.4660000801086426 C-2.2939999103546143,3.4660000801086426 -4.164000034332275,1.6460000276565552 -4.263999938964844,-0.6240000128746033 C-4.263999938964844,-0.6240000128746033 -4.263999938964844,-0.8040000200271606 -4.263999938964844,-0.8040000200271606 C-4.263999938964844,-0.8040000200271606 -4.263999938964844,-5.604000091552734 -4.263999938964844,-5.604000091552734 C-4.263999938964844,-7.953999996185303 -2.3540000915527344,-9.86400032043457 -0.004000000189989805,-9.86400032043457 Z ")
|
|
|
|
context.setBlendMode(.clear)
|
|
|
|
let _ = try? drawSvgPath(context, path: "M0.004000000189989805,-8.53600025177002 C-1.565999984741211,-8.53600025177002 -2.8459999561309814,-7.306000232696533 -2.936000108718872,-5.75600004196167 C-2.936000108718872,-5.75600004196167 -2.936000108718872,-5.5960001945495605 -2.936000108718872,-5.5960001945495605 C-2.936000108718872,-5.5960001945495605 -2.936000108718872,-0.7960000038146973 -2.936000108718872,-0.7960000038146973 C-2.936000108718872,0.8240000009536743 -1.6260000467300415,2.134000062942505 0.004000000189989805,2.134000062942505 C1.5740000009536743,2.134000062942505 2.8540000915527344,0.9039999842643738 2.934000015258789,-0.6460000276565552 C2.934000015258789,-0.6460000276565552 2.934000015258789,-0.7960000038146973 2.934000015258789,-0.7960000038146973 C2.934000015258789,-0.7960000038146973 2.934000015258789,-5.5960001945495605 2.934000015258789,-5.5960001945495605 C2.934000015258789,-7.22599983215332 1.6239999532699585,-8.53600025177002 0.004000000189989805,-8.53600025177002 Z ")
|
|
|
|
context.setBlendMode(.normal)
|
|
|
|
let _ = try? drawSvgPath(context, path: "M6.796000003814697,-1.4639999866485596 C7.165999889373779,-1.4639999866485596 7.466000080108643,-1.1640000343322754 7.466000080108643,-0.8040000200271606 C7.466000080108643,3.0959999561309814 4.47599983215332,6.296000003814697 0.6660000085830688,6.636000156402588 C0.6660000085830688,6.636000156402588 0.6660000085830688,9.196000099182129 0.6660000085830688,9.196000099182129 C0.6660000085830688,9.565999984741211 0.3659999966621399,9.866000175476074 -0.004000000189989805,9.866000175476074 C-0.33399999141693115,9.866000175476074 -0.6140000224113464,9.605999946594238 -0.6539999842643738,9.28600025177002 C-0.6539999842643738,9.28600025177002 -0.6639999747276306,9.196000099182129 -0.6639999747276306,9.196000099182129 C-0.6639999747276306,9.196000099182129 -0.6639999747276306,6.636000156402588 -0.6639999747276306,6.636000156402588 C-4.473999977111816,6.296000003814697 -7.464000225067139,3.0959999561309814 -7.464000225067139,-0.8040000200271606 C-7.464000225067139,-1.1640000343322754 -7.164000034332275,-1.4639999866485596 -6.803999900817871,-1.4639999866485596 C-6.434000015258789,-1.4639999866485596 -6.133999824523926,-1.1640000343322754 -6.133999824523926,-0.8040000200271606 C-6.133999824523926,2.5859999656677246 -3.384000062942505,5.335999965667725 -0.004000000189989805,5.335999965667725 C3.385999917984009,5.335999965667725 6.136000156402588,2.5859999656677246 6.136000156402588,-0.8040000200271606 C6.136000156402588,-1.1640000343322754 6.435999870300293,-1.4639999866485596 6.796000003814697,-1.4639999866485596 Z ")
|
|
|
|
context.translateBy(x: -18.0, y: -18.0)
|
|
|
|
if parameters.transition > 0.0 {
|
|
let startPoint: CGPoint
|
|
let endPoint: CGPoint
|
|
if parameters.reverse {
|
|
startPoint = CGPoint(x: 9.0 + 17.0 * (1.0 - parameters.transition), y: 10.0 - UIScreenPixel + 17.0 * (1.0 - parameters.transition))
|
|
endPoint = CGPoint(x: 26.0, y: 27.0 - UIScreenPixel)
|
|
} else {
|
|
startPoint = CGPoint(x: 9.0, y: 10.0 - UIScreenPixel)
|
|
endPoint = CGPoint(x: 9.0 + 17.0 * parameters.transition, y: 10.0 - UIScreenPixel + 17.0 * parameters.transition)
|
|
}
|
|
|
|
context.setBlendMode(.clear)
|
|
context.setLineWidth(4.0)
|
|
|
|
context.move(to: startPoint)
|
|
context.addLine(to: endPoint)
|
|
context.strokePath()
|
|
|
|
context.setBlendMode(.normal)
|
|
context.setStrokeColor(parameters.color.cgColor)
|
|
context.setLineWidth(1.0 + UIScreenPixel)
|
|
context.setLineCap(.round)
|
|
context.setLineJoin(.round)
|
|
|
|
context.move(to: startPoint)
|
|
context.addLine(to: endPoint)
|
|
context.strokePath()
|
|
}
|
|
}
|
|
}
|