mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Lottie improvements
This commit is contained in:
parent
73d4647439
commit
309356b8c6
@ -35,7 +35,7 @@ public final class AnimationNode : ASDisplayNode {
|
||||
|
||||
self.setViewBlock({
|
||||
if let animationName = animationName, let url = getAppBundle().url(forResource: animationName, withExtension: "json"), let animation = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = self.speed
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
@ -63,7 +63,7 @@ public final class AnimationNode : ASDisplayNode {
|
||||
|
||||
self.setViewBlock({
|
||||
if let json = try? JSONSerialization.jsonObject(with: animationData, options: []) as? [String: Any], let animation = try? Animation(dictionary: json) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = self.speed
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
|
@ -138,7 +138,7 @@ public final class LottieAnimationComponent: Component {
|
||||
self.didPlayToCompletion = false
|
||||
|
||||
if let url = getAppBundle().url(forResource: component.animation.name, withExtension: "json"), let animation = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
switch component.animation.mode {
|
||||
case .still, .animateTransitionFromPrevious:
|
||||
view.loopMode = .playOnce
|
||||
|
@ -112,7 +112,7 @@ private class StickerNode: ASDisplayNode {
|
||||
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 400.0, height: 400.0))
|
||||
|
||||
let pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
animationNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(fittedDimensions.width * 1.6), height: Int(fittedDimensions.height * 1.6), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: fittedDimensions))
|
||||
|
||||
@ -125,7 +125,8 @@ private class StickerNode: ASDisplayNode {
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
|
||||
let pathPrefix = context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(effect.resource.id)
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 2.0), height: Int(fittedDimensions.height * 2.0), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
let _ = pathPrefix
|
||||
additionalAnimationNode.setup(source: source, width: Int(fittedDimensions.width * 1.4), height: Int(fittedDimensions.height * 1.4), playbackMode: .loop, mode: .direct(cachePathPrefix: nil))
|
||||
self.additionalAnimationNode = additionalAnimationNode
|
||||
}
|
||||
} else {
|
||||
|
@ -1082,7 +1082,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
|
||||
self.addSubnode(additionalAnimationNode)
|
||||
|
||||
if !isLarge, !avatarPeers.isEmpty, let url = getAppBundle().url(forResource: "effectavatar", withExtension: "json"), let composition = Animation.filepath(url.path) {
|
||||
let view = AnimationView(animation: composition)
|
||||
let view = AnimationView(animation: composition, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.animationSpeed = 1.0
|
||||
view.backgroundColor = nil
|
||||
view.isOpaque = false
|
||||
|
@ -11,7 +11,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||
guard let url = getAppBundle().url(forResource: "LockWait", withExtension: "json"), let animation = Animation.filepath(url.path)
|
||||
else { return AnimationView() }
|
||||
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.loopMode = .autoReverse
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
@ -22,7 +22,7 @@ final class LockView: UIButton, TGModernConversationInputMicButtonLock {
|
||||
guard let url = getAppBundle().url(forResource: "Lock", withExtension: "json"), let animation = Animation.filepath(url.path)
|
||||
else { return AnimationView() }
|
||||
|
||||
let view = AnimationView(animation: animation)
|
||||
let view = AnimationView(animation: animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
view.backgroundColor = .clear
|
||||
view.isOpaque = false
|
||||
return view
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Copyright © 2021 Airbnb Inc. All rights reserved.
|
||||
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
extension CALayer {
|
||||
|
||||
@ -77,6 +78,12 @@ extension CALayer {
|
||||
let animation = CABasicAnimation(keyPath: property.caLayerKeypath)
|
||||
animation.fromValue = keyframeValue
|
||||
animation.toValue = keyframeValue
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
return animation
|
||||
}
|
||||
|
||||
@ -134,6 +141,12 @@ extension CALayer {
|
||||
let calculationMode = try self.calculationMode(for: keyframes, context: context)
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: property.caLayerKeypath)
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
|
||||
// Position animations define a `CGPath` curve that should be followed,
|
||||
// instead of animating directly between keyframe point values.
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
// MARK: - MainThreadAnimationLayer
|
||||
|
||||
@ -112,6 +113,12 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
|
||||
let animation = CABasicAnimation(keyPath: event)
|
||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
animation.fromValue = presentation()?.currentFrame
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
animation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
return animation
|
||||
}
|
||||
return super.action(forKey: event)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import QuartzCore
|
||||
import UIKit
|
||||
|
||||
// MARK: - LottieBackgroundBehavior
|
||||
|
||||
@ -393,6 +394,42 @@ final public class AnimationView: AnimationViewBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var workaroundDisplayLink: CADisplayLink?
|
||||
private var needsWorkaroundDisplayLink: Bool = false {
|
||||
didSet {
|
||||
if self.needsWorkaroundDisplayLink != oldValue {
|
||||
if self.needsWorkaroundDisplayLink {
|
||||
if workaroundDisplayLink == nil {
|
||||
class WorkaroundDisplayLinkTarget {
|
||||
private let f: () -> Void
|
||||
|
||||
init(_ f: @escaping () -> Void) {
|
||||
self.f = f
|
||||
}
|
||||
|
||||
@objc func update() {
|
||||
self.f()
|
||||
}
|
||||
}
|
||||
self.workaroundDisplayLink = CADisplayLink(target: WorkaroundDisplayLinkTarget { [weak self] in
|
||||
let _ = self?.realtimeAnimationProgress
|
||||
}, selector: #selector(WorkaroundDisplayLinkTarget.update))
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
self.workaroundDisplayLink?.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
self.workaroundDisplayLink?.add(to: .main, forMode: .common)
|
||||
}
|
||||
} else {
|
||||
if let workaroundDisplayLink = self.workaroundDisplayLink {
|
||||
self.workaroundDisplayLink = nil
|
||||
workaroundDisplayLink.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays the animation from its current state to the end.
|
||||
///
|
||||
@ -1136,6 +1173,8 @@ final public class AnimationView: AnimationViewBase {
|
||||
animationLayer?.removeAnimation(forKey: activeAnimationName)
|
||||
updateAnimationFrame(pauseFrame)
|
||||
animationContext = nil
|
||||
|
||||
self.needsWorkaroundDisplayLink = false
|
||||
}
|
||||
|
||||
/// Updates an in flight animation.
|
||||
@ -1258,6 +1297,12 @@ final public class AnimationView: AnimationViewBase {
|
||||
layerAnimation.fillMode = CAMediaTimingFillMode.both
|
||||
layerAnimation.repeatCount = loopMode.caAnimationConfiguration.repeatCount
|
||||
layerAnimation.autoreverses = loopMode.caAnimationConfiguration.autoreverses
|
||||
if #available(iOS 15.0, *) {
|
||||
let maxFps = Float(UIScreen.main.maximumFramesPerSecond)
|
||||
if maxFps > 61.0 {
|
||||
layerAnimation.preferredFrameRateRange = CAFrameRateRange(minimum: maxFps, maximum: maxFps, preferred: maxFps)
|
||||
}
|
||||
}
|
||||
|
||||
layerAnimation.isRemovedOnCompletion = false
|
||||
if timeOffset != 0 {
|
||||
@ -1269,6 +1314,7 @@ final public class AnimationView: AnimationViewBase {
|
||||
animationContext.closure.animationKey = activeAnimationName
|
||||
|
||||
animationlayer.add(layerAnimation, forKey: activeAnimationName)
|
||||
self.needsWorkaroundDisplayLink = true
|
||||
updateRasterizationState()
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ public final class CompatibleAnimationView: UIView {
|
||||
|
||||
@objc
|
||||
public init(compatibleAnimation: CompatibleAnimation) {
|
||||
animationView = AnimationView(animation: compatibleAnimation.animation)
|
||||
animationView = AnimationView(animation: compatibleAnimation.animation, configuration: LottieConfiguration(renderingEngine: .mainThread, decodingStrategy: .codable))
|
||||
self.compatibleAnimation = compatibleAnimation
|
||||
super.init(frame: .zero)
|
||||
commonInit()
|
||||
|
Loading…
x
Reference in New Issue
Block a user