mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add monkey idle animations
This commit is contained in:
parent
8ae455eb5e
commit
72fd141183
Binary file not shown.
Binary file not shown.
@ -5,6 +5,7 @@ import AsyncDisplayKit
|
||||
import RLottieBinding
|
||||
import AppBundle
|
||||
import GZip
|
||||
import SwiftSignalKit
|
||||
|
||||
private final class ManagedAnimationState {
|
||||
let item: ManagedAnimationItem
|
||||
@ -65,6 +66,7 @@ struct ManagedAnimationFrameRange: Equatable {
|
||||
struct ManagedAnimationItem: Equatable {
|
||||
let name: String
|
||||
var frames: ManagedAnimationFrameRange
|
||||
var duration: Double
|
||||
}
|
||||
|
||||
class ManagedAnimationNode: ASDisplayNode {
|
||||
@ -75,6 +77,7 @@ class ManagedAnimationNode: ASDisplayNode {
|
||||
|
||||
fileprivate var state: ManagedAnimationState?
|
||||
fileprivate var trackStack: [ManagedAnimationItem] = []
|
||||
fileprivate var didTryAdvancingState = false
|
||||
|
||||
init(size: CGSize) {
|
||||
self.intrinsicSize = size
|
||||
@ -111,7 +114,7 @@ class ManagedAnimationNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
private func advanceState() {
|
||||
func advanceState() {
|
||||
guard !self.trackStack.isEmpty else {
|
||||
return
|
||||
}
|
||||
@ -123,6 +126,8 @@ class ManagedAnimationNode: ASDisplayNode {
|
||||
} else {
|
||||
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: nil)
|
||||
}
|
||||
|
||||
self.didTryAdvancingState = false
|
||||
}
|
||||
|
||||
fileprivate func updateAnimation() {
|
||||
@ -138,10 +143,11 @@ class ManagedAnimationNode: ASDisplayNode {
|
||||
let fps = state.fps
|
||||
let frameRange = state.item.frames
|
||||
|
||||
let duration: Double = 0.3
|
||||
let duration: Double = state.item.duration
|
||||
var t = state.relativeTime / duration
|
||||
t = max(0.0, t)
|
||||
t = min(1.0, t)
|
||||
//print("\(t) \(state.item.name)")
|
||||
let frameOffset = Int(Double(frameRange.startFrame) * (1.0 - t) + Double(frameRange.endFrame) * t)
|
||||
let lowerBound: Int = 0
|
||||
let upperBound = state.frameCount - 1
|
||||
@ -155,172 +161,175 @@ class ManagedAnimationNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
var animationAdvancement: Double = 1.0 / 60.0
|
||||
animationAdvancement *= Double(self.trackStack.count + 1)
|
||||
animationAdvancement *= Double(min(2, self.trackStack.count + 1))
|
||||
|
||||
state.relativeTime += animationAdvancement
|
||||
|
||||
if state.relativeTime >= duration {
|
||||
if state.relativeTime >= duration && !self.didTryAdvancingState {
|
||||
self.didTryAdvancingState = true
|
||||
self.advanceState()
|
||||
}
|
||||
}
|
||||
|
||||
func trackTo(item: ManagedAnimationItem) {
|
||||
self.trackStack.append(item)
|
||||
self.didTryAdvancingState = false
|
||||
self.updateAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
enum ManagedMonkeyAnimationIdle: CaseIterable {
|
||||
case blink
|
||||
case ear
|
||||
case still
|
||||
}
|
||||
|
||||
enum ManagedMonkeyAnimationState: Equatable {
|
||||
case idle
|
||||
case idle(ManagedMonkeyAnimationIdle)
|
||||
case eyesClosed
|
||||
case peeking
|
||||
case tracking(CGFloat)
|
||||
}
|
||||
|
||||
/*private let animationIdle = ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle",
|
||||
intro: nil,
|
||||
loop: ManagedAnimationTrack(frameRange: 0 ..< 1),
|
||||
outro: nil
|
||||
)
|
||||
|
||||
private let animationIdle = ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle",
|
||||
intro: nil,
|
||||
loop: ManagedAnimationTrack(frameRange: 0 ..< 1),
|
||||
outro: nil
|
||||
)
|
||||
|
||||
private let animationTracking = ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking",
|
||||
intro: nil,
|
||||
loop: ManagedAnimationTrack(frameRange: 0 ..< Int.max),
|
||||
outro: nil
|
||||
)
|
||||
|
||||
private let animationHide = ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose",
|
||||
intro: ManagedAnimationTrack(frameRange: 0 ..< 41),
|
||||
loop: ManagedAnimationTrack(frameRange: 40 ..< 41),
|
||||
outro: ManagedAnimationTrack(frameRange: 60 ..< 99)
|
||||
)
|
||||
|
||||
private let animationHideNoOutro = ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose",
|
||||
intro: ManagedAnimationTrack(frameRange: 0 ..< 41),
|
||||
loop: ManagedAnimationTrack(frameRange: 40 ..< 41),
|
||||
outro: nil
|
||||
)
|
||||
|
||||
private let animationHideNoIntro = ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose",
|
||||
intro: nil,
|
||||
loop: ManagedAnimationTrack(frameRange: 40 ..< 41),
|
||||
outro: ManagedAnimationTrack(frameRange: 60 ..< 99)
|
||||
)
|
||||
|
||||
private let animationHideOutro = ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose",
|
||||
intro: nil,
|
||||
loop: nil,
|
||||
outro: ManagedAnimationTrack(frameRange: 60 ..< 99)
|
||||
)
|
||||
|
||||
private let animationPeek = ManagedAnimationItem(name: "TwoFactorSetupMonkeyPeek",
|
||||
intro: ManagedAnimationTrack(frameRange: 0 ..< 14),
|
||||
loop: ManagedAnimationTrack(frameRange: 13 ..< 14),
|
||||
outro: ManagedAnimationTrack(frameRange: 14 ..< 34)
|
||||
)
|
||||
|
||||
private let animationMail = ManagedAnimationItem(name: "TwoFactorSetupMail",
|
||||
intro: ManagedAnimationTrack(frameRange: 0 ..< Int.max),
|
||||
loop: ManagedAnimationTrack(frameRange: Int.max - 1 ..< Int.max),
|
||||
outro: nil
|
||||
)
|
||||
|
||||
private let animationHint = ManagedAnimationItem(name: "TwoFactorSetupHint",
|
||||
intro: ManagedAnimationTrack(frameRange: 0 ..< Int.max),
|
||||
loop: ManagedAnimationTrack(frameRange: Int.max - 1 ..< Int.max),
|
||||
outro: nil
|
||||
)*/
|
||||
|
||||
final class ManagedMonkeyAnimationNode: ManagedAnimationNode {
|
||||
private var monkeyState: ManagedMonkeyAnimationState = .idle
|
||||
private var monkeyState: ManagedMonkeyAnimationState = .idle(.blink)
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
|
||||
init() {
|
||||
super.init(size: CGSize(width: 136.0, height: 136.0))
|
||||
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 0)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 0), duration: 0.3))
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.timer?.invalidate()
|
||||
}
|
||||
|
||||
private func startIdleTimer() {
|
||||
self.timer?.invalidate()
|
||||
let timer = SwiftSignalKit.Timer(timeout: Double.random(in: 1.0 ..< 1.5), repeat: false, completion: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch strongSelf.monkeyState {
|
||||
case .idle:
|
||||
if let idle = ManagedMonkeyAnimationIdle.allCases.randomElement() {
|
||||
strongSelf.setState(.idle(idle))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, queue: .mainQueue())
|
||||
self.timer = timer
|
||||
timer.start()
|
||||
}
|
||||
|
||||
override func advanceState() {
|
||||
super.advanceState()
|
||||
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
|
||||
if self.trackStack.isEmpty, case .idle = self.monkeyState {
|
||||
self.startIdleTimer()
|
||||
}
|
||||
}
|
||||
|
||||
private func enqueueIdle(_ idle: ManagedMonkeyAnimationIdle) {
|
||||
switch idle {
|
||||
case .still:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 0), duration: 0.3))
|
||||
case .blink:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle1", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 30), duration: 0.3))
|
||||
case .ear:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle2", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 30), duration: 0.3))
|
||||
//self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 179), duration: 3.0))
|
||||
}
|
||||
}
|
||||
|
||||
func setState(_ monkeyState: ManagedMonkeyAnimationState) {
|
||||
let previousState = self.monkeyState
|
||||
self.monkeyState = monkeyState
|
||||
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
|
||||
func enqueueTracking(_ value: CGFloat) {
|
||||
let lowerBound = 18
|
||||
let upperBound = 160
|
||||
let frameIndex = lowerBound + Int(value * CGFloat(upperBound - lowerBound))
|
||||
if let state = self.state, state.item.name == "TwoFactorSetupMonkeyTracking" {
|
||||
let item = ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: state.frameIndex ?? 0, endFrame: frameIndex))
|
||||
let item = ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: state.frameIndex ?? 0, endFrame: frameIndex), duration: 0.3)
|
||||
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: state)
|
||||
self.didTryAdvancingState = false
|
||||
self.updateAnimation()
|
||||
} else {
|
||||
self.trackStack = self.trackStack.filter {
|
||||
$0.name != "TwoFactorSetupMonkeyTracking"
|
||||
}
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: frameIndex)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: frameIndex), duration: 0.3))
|
||||
}
|
||||
}
|
||||
|
||||
func enqueueClearTracking() {
|
||||
if let state = self.state, state.item.name == "TwoFactorSetupMonkeyTracking" {
|
||||
let item = ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: state.frameIndex ?? 0, endFrame: 0))
|
||||
let item = ManagedAnimationItem(name: "TwoFactorSetupMonkeyTracking", frames: ManagedAnimationFrameRange(startFrame: state.frameIndex ?? 0, endFrame: 0), duration: 0.3)
|
||||
self.state = ManagedAnimationState(displaySize: self.intrinsicSize, item: item, current: state)
|
||||
self.didTryAdvancingState = false
|
||||
self.updateAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
switch previousState {
|
||||
case .idle:
|
||||
case let .idle(previousIdle):
|
||||
switch monkeyState {
|
||||
case .idle:
|
||||
break
|
||||
case let .idle(idle):
|
||||
self.enqueueIdle(idle)
|
||||
case .eyesClosed:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41), duration: 0.3))
|
||||
case .peeking:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41), duration: 0.3))
|
||||
case let .tracking(value):
|
||||
enqueueTracking(value)
|
||||
}
|
||||
case .eyesClosed:
|
||||
switch monkeyState {
|
||||
case .idle:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0)))
|
||||
case let .idle(idle):
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0), duration: 0.3))
|
||||
self.enqueueIdle(idle)
|
||||
case .eyesClosed:
|
||||
break
|
||||
case .peeking:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 14)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 14), duration: 0.3))
|
||||
case let .tracking(value):
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0), duration: 0.3))
|
||||
enqueueTracking(value)
|
||||
}
|
||||
case .peeking:
|
||||
switch monkeyState {
|
||||
case .idle:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0)))
|
||||
case let .idle(idle):
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0), duration: 0.3))
|
||||
self.enqueueIdle(idle)
|
||||
case .eyesClosed:
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyPeek", frames: ManagedAnimationFrameRange(startFrame: 14, endFrame: 0)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyPeek", frames: ManagedAnimationFrameRange(startFrame: 14, endFrame: 0), duration: 0.3))
|
||||
case .peeking:
|
||||
break
|
||||
case let .tracking(value):
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 41, endFrame: 0), duration: 0.3))
|
||||
enqueueTracking(value)
|
||||
}
|
||||
case let .tracking(currentValue):
|
||||
switch monkeyState {
|
||||
case .idle:
|
||||
case let .idle(idle):
|
||||
enqueueClearTracking()
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 0)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyIdle", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 0), duration: 0.3))
|
||||
self.enqueueIdle(idle)
|
||||
case .eyesClosed:
|
||||
enqueueClearTracking()
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyClose", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41), duration: 0.3))
|
||||
case .peeking:
|
||||
enqueueClearTracking()
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41)))
|
||||
self.trackTo(item: ManagedAnimationItem(name: "TwoFactorSetupMonkeyCloseAndPeek", frames: ManagedAnimationFrameRange(startFrame: 0, endFrame: 41), duration: 0.3))
|
||||
case let .tracking(value):
|
||||
if abs(currentValue - value) > CGFloat.ulpOfOne {
|
||||
enqueueTracking(value)
|
||||
|
@ -967,14 +967,14 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
} else if strongSelf.inputNodes[0].isFocused {
|
||||
let hasText = !strongSelf.inputNodes[0].text.isEmpty
|
||||
if !hasText {
|
||||
strongSelf.monkeyNode?.setState(.idle)
|
||||
strongSelf.monkeyNode?.setState(.idle(.still))
|
||||
} else if textHidden {
|
||||
strongSelf.monkeyNode?.setState(.eyesClosed)
|
||||
} else {
|
||||
strongSelf.monkeyNode?.setState(.peeking)
|
||||
}
|
||||
} else {
|
||||
strongSelf.monkeyNode?.setState(.idle)
|
||||
strongSelf.monkeyNode?.setState(.idle(.still))
|
||||
}
|
||||
case .emailAddress:
|
||||
if strongSelf.inputNodes[0].isFocused {
|
||||
@ -990,7 +990,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
trackingOffset = max(0.0, min(1.0, trackingOffset))
|
||||
strongSelf.monkeyNode?.setState(.tracking(trackingOffset))
|
||||
} else {
|
||||
strongSelf.monkeyNode?.setState(.idle)
|
||||
strongSelf.monkeyNode?.setState(.idle(.still))
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
Loading…
x
Reference in New Issue
Block a user