Lottie improvements

This commit is contained in:
Ali 2022-06-07 13:31:47 +04:00
parent 73d4647439
commit 309356b8c6
9 changed files with 76 additions and 9 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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()
}

View File

@ -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()