mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Story mute button animation
This commit is contained in:
parent
1cee5070f8
commit
a257155ab4
@ -28,6 +28,10 @@ private final class LottieDirectContent: LottieComponent.Content {
|
|||||||
self.path = path
|
self.path = path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override var frameRange: Range<Double> {
|
||||||
|
return 0.0 ..< 1.0
|
||||||
|
}
|
||||||
|
|
||||||
override func isEqual(to other: LottieComponent.Content) -> Bool {
|
override func isEqual(to other: LottieComponent.Content) -> Bool {
|
||||||
guard let other = other as? LottieDirectContent else {
|
guard let other = other as? LottieDirectContent else {
|
||||||
return false
|
return false
|
||||||
|
@ -12,6 +12,10 @@ public final class LottieComponent: Component {
|
|||||||
public typealias EnvironmentType = Empty
|
public typealias EnvironmentType = Empty
|
||||||
|
|
||||||
open class Content: Equatable {
|
open class Content: Equatable {
|
||||||
|
open var frameRange: Range<Double> {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,8 +38,14 @@ public final class LottieComponent: Component {
|
|||||||
public final class AppBundleContent: Content {
|
public final class AppBundleContent: Content {
|
||||||
public let name: String
|
public let name: String
|
||||||
|
|
||||||
public init(name: String) {
|
private let frameRangeValue: Range<Double>
|
||||||
|
override public var frameRange: Range<Double> {
|
||||||
|
return self.frameRangeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(name: String, frameRange: Range<Double> = 0.0 ..< 1.0) {
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.frameRangeValue = frameRange
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func isEqual(to other: Content) -> Bool {
|
override public func isEqual(to other: Content) -> Bool {
|
||||||
@ -45,6 +55,9 @@ public final class LottieComponent: Component {
|
|||||||
if self.name != other.name {
|
if self.name != other.name {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if self.frameRangeValue != other.frameRangeValue {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +72,10 @@ public final class LottieComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StartingPosition {
|
public enum StartingPosition: Equatable {
|
||||||
case begin
|
case begin
|
||||||
case end
|
case end
|
||||||
|
case fraction(Double)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let content: Content
|
public let content: Content
|
||||||
@ -104,6 +118,7 @@ public final class LottieComponent: Component {
|
|||||||
private var scheduledPlayOnce: Bool = false
|
private var scheduledPlayOnce: Bool = false
|
||||||
private var playOnceCompletion: (() -> Void)?
|
private var playOnceCompletion: (() -> Void)?
|
||||||
private var animationInstance: LottieInstance?
|
private var animationInstance: LottieInstance?
|
||||||
|
private var animationFrameRange: Range<Int>?
|
||||||
private var currentDisplaySize: CGSize?
|
private var currentDisplaySize: CGSize?
|
||||||
private var currentContentDisposable: Disposable?
|
private var currentContentDisposable: Disposable?
|
||||||
|
|
||||||
@ -171,7 +186,7 @@ public final class LottieComponent: Component {
|
|||||||
public func playOnce(delay: Double = 0.0, completion: (() -> Void)? = nil) {
|
public func playOnce(delay: Double = 0.0, completion: (() -> Void)? = nil) {
|
||||||
self.playOnceCompletion = completion
|
self.playOnceCompletion = completion
|
||||||
|
|
||||||
guard let _ = self.animationInstance else {
|
guard let _ = self.animationInstance, let animationFrameRange = self.animationFrameRange else {
|
||||||
self.scheduledPlayOnce = true
|
self.scheduledPlayOnce = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -182,8 +197,8 @@ public final class LottieComponent: Component {
|
|||||||
|
|
||||||
self.scheduledPlayOnce = false
|
self.scheduledPlayOnce = false
|
||||||
|
|
||||||
if self.currentFrame != 0 {
|
if self.currentFrame != animationFrameRange.lowerBound {
|
||||||
self.currentFrame = 0
|
self.currentFrame = animationFrameRange.lowerBound
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,24 +233,35 @@ public final class LottieComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadAnimation(data: Data, cacheKey: String?, startingPosition: StartingPosition) {
|
private func loadAnimation(data: Data, cacheKey: String?, startingPosition: StartingPosition, frameRange: Range<Double>) {
|
||||||
self.animationInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: cacheKey ?? "")
|
self.animationInstance = LottieInstance(data: data, fitzModifier: .none, colorReplacements: nil, cacheKey: cacheKey ?? "")
|
||||||
|
if let animationInstance = self.animationInstance {
|
||||||
|
self.animationFrameRange = Int(floor(frameRange.lowerBound * Double(animationInstance.frameCount))) ..< Int(floor(frameRange.upperBound * Double(animationInstance.frameCount)))
|
||||||
|
} else {
|
||||||
|
self.animationFrameRange = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let _ = self.animationInstance, let animationFrameRange = self.animationFrameRange {
|
||||||
|
switch startingPosition {
|
||||||
|
case .begin:
|
||||||
|
self.currentFrame = animationFrameRange.lowerBound
|
||||||
|
case .end:
|
||||||
|
self.currentFrame = Int(max(animationFrameRange.lowerBound, animationFrameRange.upperBound - 1))
|
||||||
|
case let .fraction(fraction):
|
||||||
|
self.currentFrame = animationFrameRange.lowerBound + Int(floor(Double(animationFrameRange.upperBound - animationFrameRange.lowerBound) * fraction))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.scheduledPlayOnce {
|
if self.scheduledPlayOnce {
|
||||||
self.scheduledPlayOnce = false
|
self.scheduledPlayOnce = false
|
||||||
self.playOnce()
|
self.playOnce()
|
||||||
} else if let animationInstance = self.animationInstance {
|
} else {
|
||||||
switch startingPosition {
|
|
||||||
case .begin:
|
|
||||||
self.currentFrame = 0
|
|
||||||
case .end:
|
|
||||||
self.currentFrame = Int(animationInstance.frameCount - 1)
|
|
||||||
}
|
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func advanceIfNeeded() {
|
private func advanceIfNeeded() {
|
||||||
guard let animationInstance = self.animationInstance else {
|
guard let animationInstance = self.animationInstance, let animationFrameRange = self.animationFrameRange else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let currentFrameStartTime = self.currentFrameStartTime else {
|
guard let currentFrameStartTime = self.currentFrameStartTime else {
|
||||||
@ -258,8 +284,8 @@ public final class LottieComponent: Component {
|
|||||||
advanceFrameCount = 4
|
advanceFrameCount = 4
|
||||||
}
|
}
|
||||||
self.currentFrame += advanceFrameCount
|
self.currentFrame += advanceFrameCount
|
||||||
if self.currentFrame >= Int(animationInstance.frameCount) - 1 {
|
if self.currentFrame >= animationFrameRange.upperBound - 1 {
|
||||||
self.currentFrame = Int(animationInstance.frameCount) - 1
|
self.currentFrame = animationFrameRange.upperBound - 1
|
||||||
self.updateImage()
|
self.updateImage()
|
||||||
self.displayLink?.invalidate()
|
self.displayLink?.invalidate()
|
||||||
self.displayLink = nil
|
self.displayLink = nil
|
||||||
@ -276,14 +302,17 @@ public final class LottieComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateImage() {
|
private func updateImage() {
|
||||||
guard let animationInstance = self.animationInstance, let currentDisplaySize = self.currentDisplaySize else {
|
guard let animationInstance = self.animationInstance, let animationFrameRange = self.animationFrameRange, let currentDisplaySize = self.currentDisplaySize else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let context = DrawingContext(size: currentDisplaySize, scale: 1.0, opaque: false, clear: true) else {
|
guard let context = DrawingContext(size: currentDisplaySize, scale: 1.0, opaque: false, clear: true) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
animationInstance.renderFrame(with: Int32(self.currentFrame % Int(animationInstance.frameCount)), into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(currentDisplaySize.width), height: Int32(currentDisplaySize.height), bytesPerRow: Int32(context.bytesPerRow))
|
var effectiveFrameIndex = self.currentFrame
|
||||||
|
effectiveFrameIndex = max(animationFrameRange.lowerBound, min(animationFrameRange.upperBound, effectiveFrameIndex))
|
||||||
|
|
||||||
|
animationInstance.renderFrame(with: Int32(effectiveFrameIndex), into: context.bytes.assumingMemoryBound(to: UInt8.self), width: Int32(currentDisplaySize.width), height: Int32(currentDisplaySize.height), bytesPerRow: Int32(context.bytesPerRow))
|
||||||
|
|
||||||
var image = context.generateImage()
|
var image = context.generateImage()
|
||||||
if let _ = self.component?.color {
|
if let _ = self.component?.color {
|
||||||
@ -316,12 +345,13 @@ public final class LottieComponent: Component {
|
|||||||
if previousComponent?.content != component.content {
|
if previousComponent?.content != component.content {
|
||||||
self.currentContentDisposable?.dispose()
|
self.currentContentDisposable?.dispose()
|
||||||
let content = component.content
|
let content = component.content
|
||||||
|
let frameRange = content.frameRange
|
||||||
self.currentContentDisposable = component.content.load { [weak self, weak content] data, cacheKey in
|
self.currentContentDisposable = component.content.load { [weak self, weak content] data, cacheKey in
|
||||||
Queue.mainQueue().async {
|
Queue.mainQueue().async {
|
||||||
guard let self, let component = self.component, component.content == content else {
|
guard let self, let component = self.component, component.content == content else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.loadAnimation(data: data, cacheKey: cacheKey, startingPosition: component.startingPosition)
|
self.loadAnimation(data: data, cacheKey: cacheKey, startingPosition: component.startingPosition, frameRange: frameRange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if redrawImage {
|
} else if redrawImage {
|
||||||
|
@ -9,6 +9,10 @@ public extension LottieComponent {
|
|||||||
final class EmojiContent: LottieComponent.Content {
|
final class EmojiContent: LottieComponent.Content {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let fileId: Int64
|
private let fileId: Int64
|
||||||
|
|
||||||
|
override public var frameRange: Range<Double> {
|
||||||
|
return 0.0 ..< 1.0
|
||||||
|
}
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
|
@ -47,6 +47,10 @@ public final class PlainButtonComponent: Component {
|
|||||||
private let contentContainer = UIView()
|
private let contentContainer = UIView()
|
||||||
private let content = ComponentView<Empty>()
|
private let content = ComponentView<Empty>()
|
||||||
|
|
||||||
|
public var contentView: UIView? {
|
||||||
|
return self.content.view
|
||||||
|
}
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ swift_library(
|
|||||||
"//submodules/WebPBinding",
|
"//submodules/WebPBinding",
|
||||||
"//submodules/Utils/RangeSet",
|
"//submodules/Utils/RangeSet",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryStealthModeSheetScreen",
|
||||||
|
"//submodules/TelegramUI/Components/LottieComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -35,6 +35,7 @@ import AttachmentUI
|
|||||||
import StickerPackPreviewUI
|
import StickerPackPreviewUI
|
||||||
import TextNodeWithEntities
|
import TextNodeWithEntities
|
||||||
import TelegramStringFormatting
|
import TelegramStringFormatting
|
||||||
|
import LottieComponent
|
||||||
|
|
||||||
public final class StoryAvailableReactions: Equatable {
|
public final class StoryAvailableReactions: Equatable {
|
||||||
let reactionItems: [ReactionItem]
|
let reactionItems: [ReactionItem]
|
||||||
@ -372,6 +373,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
var leftInfoItem: InfoItem?
|
var leftInfoItem: InfoItem?
|
||||||
|
|
||||||
let moreButton = ComponentView<Empty>()
|
let moreButton = ComponentView<Empty>()
|
||||||
|
var currentSoundButtonState: Bool?
|
||||||
let soundButton = ComponentView<Empty>()
|
let soundButton = ComponentView<Empty>()
|
||||||
var privacyIcon: ComponentView<Empty>?
|
var privacyIcon: ComponentView<Empty>?
|
||||||
|
|
||||||
@ -2427,21 +2429,18 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let soundImage: String
|
let soundButtonState = isSilentVideo || component.isAudioMuted
|
||||||
if isSilentVideo || component.isAudioMuted {
|
|
||||||
soundImage = "Stories/SoundOff"
|
|
||||||
} else {
|
|
||||||
soundImage = "Stories/SoundOn"
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO:anim_storymute
|
|
||||||
let soundButtonSize = self.soundButton.update(
|
let soundButtonSize = self.soundButton.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(PlainButtonComponent(
|
component: AnyComponent(PlainButtonComponent(
|
||||||
content: AnyComponent(BundleIconComponent(
|
content: AnyComponent(LottieComponent(
|
||||||
name: soundImage,
|
content: LottieComponent.AppBundleContent(
|
||||||
tintColor: .white,
|
name: "anim_storymute",
|
||||||
maxSize: nil
|
frameRange: soundButtonState ? (0.0 ..< 0.5) : (0.5 ..< 1.0)
|
||||||
|
),
|
||||||
|
color: .white,
|
||||||
|
startingPosition: .end,
|
||||||
|
size: CGSize(width: 30.0, height: 30.0)
|
||||||
)),
|
)),
|
||||||
effectAlignment: .center,
|
effectAlignment: .center,
|
||||||
minSize: CGSize(width: 33.0, height: 64.0),
|
minSize: CGSize(width: 33.0, height: 64.0),
|
||||||
@ -2481,6 +2480,13 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if isVideo {
|
if isVideo {
|
||||||
headerRightOffset -= soundButtonSize.width + 13.0
|
headerRightOffset -= soundButtonSize.width + 13.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let currentSoundButtonState = self.currentSoundButtonState, currentSoundButtonState != soundButtonState {
|
||||||
|
if let lottieView = (soundButtonView as? PlainButtonComponent.View)?.contentView as? LottieComponent.View {
|
||||||
|
lottieView.playOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.currentSoundButtonState = soundButtonState
|
||||||
}
|
}
|
||||||
|
|
||||||
let storyPrivacyIcon: StoryPrivacyIconComponent.Privacy?
|
let storyPrivacyIcon: StoryPrivacyIconComponent.Privacy?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user