refactor: 将媒体加载和准备逻辑从视图迁移至 ViewModel
This commit is contained in:
parent
5c25d0bf4c
commit
552193b4c1
@ -5,11 +5,13 @@ struct LottieView: UIViewRepresentable {
|
|||||||
let name: String
|
let name: String
|
||||||
let loopMode: LottieLoopMode
|
let loopMode: LottieLoopMode
|
||||||
let animationSpeed: CGFloat
|
let animationSpeed: CGFloat
|
||||||
|
let isPlaying: Bool
|
||||||
|
|
||||||
init(name: String, loopMode: LottieLoopMode = .loop, animationSpeed: CGFloat = 1.0) {
|
init(name: String, loopMode: LottieLoopMode = .loop, animationSpeed: CGFloat = 1.0, isPlaying: Bool = true) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.loopMode = loopMode
|
self.loopMode = loopMode
|
||||||
self.animationSpeed = animationSpeed
|
self.animationSpeed = animationSpeed
|
||||||
|
self.isPlaying = isPlaying
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUIView(context: Context) -> LottieAnimationView {
|
func makeUIView(context: Context) -> LottieAnimationView {
|
||||||
@ -31,16 +33,26 @@ struct LottieView: UIViewRepresentable {
|
|||||||
animationView.contentMode = .scaleAspectFit
|
animationView.contentMode = .scaleAspectFit
|
||||||
animationView.backgroundBehavior = .pauseAndRestore
|
animationView.backgroundBehavior = .pauseAndRestore
|
||||||
|
|
||||||
// 播放动画
|
// 播放/暂停
|
||||||
animationView.play()
|
if isPlaying {
|
||||||
|
animationView.play()
|
||||||
|
} else {
|
||||||
|
animationView.pause()
|
||||||
|
}
|
||||||
|
|
||||||
return animationView
|
return animationView
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: LottieAnimationView, context: Context) {
|
func updateUIView(_ uiView: LottieAnimationView, context: Context) {
|
||||||
// 确保动画持续播放
|
// 根据 isPlaying 控制播放/暂停
|
||||||
if !uiView.isAnimationPlaying {
|
if isPlaying {
|
||||||
uiView.play()
|
if !uiView.isAnimationPlaying {
|
||||||
|
uiView.play()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if uiView.isAnimationPlaying {
|
||||||
|
uiView.pause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
21
wake/Utils/Performance.swift
Normal file
21
wake/Utils/Performance.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Foundation
|
||||||
|
import os
|
||||||
|
|
||||||
|
enum Perf {
|
||||||
|
private static let log = OSLog(subsystem: "app.wake", category: "performance")
|
||||||
|
|
||||||
|
static func event(_ name: StaticString) {
|
||||||
|
os_signpost(.event, log: log, name: name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
static func begin(_ name: StaticString) -> OSSignpostID {
|
||||||
|
let id = OSSignpostID(log: log)
|
||||||
|
os_signpost(.begin, log: log, name: name, signpostID: id)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
static func end(_ name: StaticString, id: OSSignpostID) {
|
||||||
|
os_signpost(.end, log: log, name: name, signpostID: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
import UIKit
|
||||||
|
import AVKit
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class BlindBoxViewModel: ObservableObject {
|
final class BlindBoxViewModel: ObservableObject {
|
||||||
@ -19,6 +21,11 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
@Published var imageURL: String = ""
|
@Published var imageURL: String = ""
|
||||||
@Published var didBootstrap: Bool = false
|
@Published var didBootstrap: Bool = false
|
||||||
@Published var countdownText: String = ""
|
@Published var countdownText: String = ""
|
||||||
|
// Media prepared for display
|
||||||
|
@Published var player: AVPlayer? = nil
|
||||||
|
@Published var displayImage: UIImage? = nil
|
||||||
|
@Published var aspectRatio: CGFloat = 1.0
|
||||||
|
@Published var isPortrait: Bool = false
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
private var pollingTask: Task<Void, Never>? = nil
|
private var pollingTask: Task<Void, Never>? = nil
|
||||||
@ -31,10 +38,12 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func load() async {
|
func load() async {
|
||||||
|
Perf.event("BlindVM_Load_Begin")
|
||||||
await bootstrapInitialState()
|
await bootstrapInitialState()
|
||||||
await startPolling()
|
await startPolling()
|
||||||
loadMemberProfile()
|
loadMemberProfile()
|
||||||
await loadBlindCount()
|
await loadBlindCount()
|
||||||
|
Perf.event("BlindVM_Load_End")
|
||||||
}
|
}
|
||||||
|
|
||||||
func startPolling() async {
|
func startPolling() async {
|
||||||
@ -47,6 +56,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
do {
|
do {
|
||||||
for try await data in BlindBoxPolling.singleBox(boxId: boxId, intervalSeconds: 2.0) {
|
for try await data in BlindBoxPolling.singleBox(boxId: boxId, intervalSeconds: 2.0) {
|
||||||
|
Perf.event("BlindVM_Poll_Single_Yield")
|
||||||
print("[VM] SingleBox polled status: \(data.status)")
|
print("[VM] SingleBox polled status: \(data.status)")
|
||||||
self.blindGenerate = data
|
self.blindGenerate = data
|
||||||
if self.mediaType == .image {
|
if self.mediaType == .image {
|
||||||
@ -55,6 +65,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
self.videoURL = data.resultFile?.url ?? ""
|
self.videoURL = data.resultFile?.url ?? ""
|
||||||
}
|
}
|
||||||
self.applyStatusSideEffects()
|
self.applyStatusSideEffects()
|
||||||
|
Task { await self.prepareMedia() }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} catch is CancellationError {
|
} catch is CancellationError {
|
||||||
@ -69,6 +80,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
do {
|
do {
|
||||||
for try await item in BlindBoxPolling.firstUnopened(intervalSeconds: 2.0) {
|
for try await item in BlindBoxPolling.firstUnopened(intervalSeconds: 2.0) {
|
||||||
|
Perf.event("BlindVM_Poll_List_Yield")
|
||||||
print("[VM] List polled first unopened: id=\(item.id ?? "nil"), status=\(item.status)")
|
print("[VM] List polled first unopened: id=\(item.id ?? "nil"), status=\(item.status)")
|
||||||
self.blindGenerate = item
|
self.blindGenerate = item
|
||||||
if self.mediaType == .image {
|
if self.mediaType == .image {
|
||||||
@ -77,6 +89,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
self.videoURL = item.resultFile?.url ?? ""
|
self.videoURL = item.resultFile?.url ?? ""
|
||||||
}
|
}
|
||||||
self.applyStatusSideEffects()
|
self.applyStatusSideEffects()
|
||||||
|
Task { await self.prepareMedia() }
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} catch is CancellationError {
|
} catch is CancellationError {
|
||||||
@ -100,6 +113,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
self.videoURL = data.resultFile?.url ?? ""
|
self.videoURL = data.resultFile?.url ?? ""
|
||||||
}
|
}
|
||||||
self.applyStatusSideEffects()
|
self.applyStatusSideEffects()
|
||||||
|
Task { await self.prepareMedia() }
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print("❌ bootstrapInitialState (single) failed: \(error)")
|
print("❌ bootstrapInitialState (single) failed: \(error)")
|
||||||
@ -119,6 +133,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
self.videoURL = item.resultFile?.url ?? ""
|
self.videoURL = item.resultFile?.url ?? ""
|
||||||
}
|
}
|
||||||
self.applyStatusSideEffects()
|
self.applyStatusSideEffects()
|
||||||
|
Task { await self.prepareMedia() }
|
||||||
} else if let first = list?.first {
|
} else if let first = list?.first {
|
||||||
// 没有 Unopened,选取第一个用于展示状态(通常是 Preparing)
|
// 没有 Unopened,选取第一个用于展示状态(通常是 Preparing)
|
||||||
self.blindGenerate = first
|
self.blindGenerate = first
|
||||||
@ -130,6 +145,7 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
// 标记首帧状态已准备,供视图决定是否显示 loading/ready
|
// 标记首帧状态已准备,供视图决定是否显示 loading/ready
|
||||||
self.didBootstrap = true
|
self.didBootstrap = true
|
||||||
|
Perf.event("BlindVM_Bootstrap_Done")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopPolling() {
|
func stopPolling() {
|
||||||
@ -212,4 +228,35 @@ final class BlindBoxViewModel: ObservableObject {
|
|||||||
countdownTask?.cancel()
|
countdownTask?.cancel()
|
||||||
countdownTask = nil
|
countdownTask = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Media Preparation
|
||||||
|
func prepareMedia() async {
|
||||||
|
if mediaType == .all {
|
||||||
|
// Video path
|
||||||
|
guard !videoURL.isEmpty, let url = URL(string: videoURL) else { return }
|
||||||
|
let asset = AVAsset(url: url)
|
||||||
|
let item = AVPlayerItem(asset: asset)
|
||||||
|
let player = AVPlayer(playerItem: item)
|
||||||
|
if let track = asset.tracks(withMediaType: .video).first {
|
||||||
|
let size = track.naturalSize.applying(track.preferredTransform)
|
||||||
|
let width = abs(size.width)
|
||||||
|
let height = abs(size.height)
|
||||||
|
self.aspectRatio = height == 0 ? 1.0 : width / height
|
||||||
|
self.isPortrait = height > width
|
||||||
|
}
|
||||||
|
self.player = player
|
||||||
|
} else if mediaType == .image {
|
||||||
|
guard !imageURL.isEmpty, let url = URL(string: imageURL) else { return }
|
||||||
|
do {
|
||||||
|
let (data, _) = try await URLSession.shared.data(from: url)
|
||||||
|
if let image = UIImage(data: data) {
|
||||||
|
self.displayImage = image
|
||||||
|
self.aspectRatio = image.size.height == 0 ? 1.0 : image.size.width / image.size.height
|
||||||
|
self.isPortrait = image.size.height > image.size.width
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("⚠️ prepareMedia image load failed: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,12 +80,8 @@ struct BlindBoxView: View {
|
|||||||
@State private var showScalingOverlay = false
|
@State private var showScalingOverlay = false
|
||||||
@State private var animationPhase: BlindBoxAnimationPhase = .none
|
@State private var animationPhase: BlindBoxAnimationPhase = .none
|
||||||
@State private var scale: CGFloat = 0.1
|
@State private var scale: CGFloat = 0.1
|
||||||
@State private var videoPlayer: AVPlayer?
|
|
||||||
@State private var showControls = false
|
@State private var showControls = false
|
||||||
@State private var isAnimating = true
|
@State private var isAnimating = true
|
||||||
@State private var aspectRatio: CGFloat = 1.0
|
|
||||||
@State private var isPortrait: Bool = false
|
|
||||||
@State private var displayImage: UIImage?
|
|
||||||
@State private var showMedia = false
|
@State private var showMedia = false
|
||||||
|
|
||||||
// 查询数据 - 简单查询
|
// 查询数据 - 简单查询
|
||||||
@ -107,90 +103,7 @@ struct BlindBoxView: View {
|
|||||||
|
|
||||||
// 已迁移至 ViewModel
|
// 已迁移至 ViewModel
|
||||||
|
|
||||||
private func loadImage() {
|
// 本地媒体加载逻辑已迁移至 ViewModel.prepareMedia()
|
||||||
guard !viewModel.imageURL.isEmpty, let url = URL(string: viewModel.imageURL) else {
|
|
||||||
print("⚠️ 图片URL无效或为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
URLSession.shared.dataTask(with: url) { data, _, _ in
|
|
||||||
if let data = data, let image = UIImage(data: data) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayImage = image
|
|
||||||
self.aspectRatio = image.size.width / image.size.height
|
|
||||||
self.isPortrait = image.size.height > image.size.width
|
|
||||||
self.showScalingOverlay = true // 确保显示媒体内容
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadVideo() {
|
|
||||||
guard !viewModel.videoURL.isEmpty, let url = URL(string: viewModel.videoURL) else {
|
|
||||||
print("⚠️ 视频URL无效或为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let asset = AVAsset(url: url)
|
|
||||||
let playerItem = AVPlayerItem(asset: asset)
|
|
||||||
let player = AVPlayer(playerItem: playerItem)
|
|
||||||
|
|
||||||
let videoTracks = asset.tracks(withMediaType: .video)
|
|
||||||
if let videoTrack = videoTracks.first {
|
|
||||||
let size = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
|
|
||||||
let width = abs(size.width)
|
|
||||||
let height = abs(size.height)
|
|
||||||
|
|
||||||
aspectRatio = width / height
|
|
||||||
isPortrait = height > width
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新视频播放器
|
|
||||||
videoPlayer = player
|
|
||||||
videoPlayer?.play()
|
|
||||||
showScalingOverlay = true // 确保显示媒体内容
|
|
||||||
}
|
|
||||||
|
|
||||||
private func prepareVideo() {
|
|
||||||
guard !viewModel.videoURL.isEmpty, let url = URL(string: viewModel.videoURL) else {
|
|
||||||
print("⚠️ 视频URL无效或为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let asset = AVAsset(url: url)
|
|
||||||
let playerItem = AVPlayerItem(asset: asset)
|
|
||||||
let player = AVPlayer(playerItem: playerItem)
|
|
||||||
|
|
||||||
let videoTracks = asset.tracks(withMediaType: .video)
|
|
||||||
if let videoTrack = videoTracks.first {
|
|
||||||
let size = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
|
|
||||||
let width = abs(size.width)
|
|
||||||
let height = abs(size.height)
|
|
||||||
|
|
||||||
aspectRatio = width / height
|
|
||||||
isPortrait = height > width
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新视频播放器
|
|
||||||
videoPlayer = player
|
|
||||||
}
|
|
||||||
|
|
||||||
private func prepareImage() {
|
|
||||||
guard !viewModel.imageURL.isEmpty, let url = URL(string: viewModel.imageURL) else {
|
|
||||||
print("⚠️ 图片URL无效或为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
URLSession.shared.dataTask(with: url) { data, _, _ in
|
|
||||||
if let data = data, let image = UIImage(data: data) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.displayImage = image
|
|
||||||
self.aspectRatio = image.size.width / image.size.height
|
|
||||||
self.isPortrait = image.size.height > image.size.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func startScalingAnimation() {
|
private func startScalingAnimation() {
|
||||||
self.scale = 0.1
|
self.scale = 0.1
|
||||||
@ -203,18 +116,18 @@ struct BlindBoxView: View {
|
|||||||
|
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
private var scaledWidth: CGFloat {
|
private var scaledWidth: CGFloat {
|
||||||
if isPortrait {
|
if viewModel.isPortrait {
|
||||||
return UIScreen.main.bounds.height * scale * 1/aspectRatio
|
return UIScreen.main.bounds.height * scale * 1/viewModel.aspectRatio
|
||||||
} else {
|
} else {
|
||||||
return UIScreen.main.bounds.width * scale
|
return UIScreen.main.bounds.width * scale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var scaledHeight: CGFloat {
|
private var scaledHeight: CGFloat {
|
||||||
if isPortrait {
|
if viewModel.isPortrait {
|
||||||
return UIScreen.main.bounds.height * scale
|
return UIScreen.main.bounds.height * scale
|
||||||
} else {
|
} else {
|
||||||
return UIScreen.main.bounds.width * scale * 1/aspectRatio
|
return UIScreen.main.bounds.width * scale * 1/viewModel.aspectRatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +135,7 @@ struct BlindBoxView: View {
|
|||||||
ZStack {
|
ZStack {
|
||||||
Color.themeTextWhiteSecondary.ignoresSafeArea()
|
Color.themeTextWhiteSecondary.ignoresSafeArea()
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
Perf.event("BlindBox_Appear")
|
||||||
print("🎯 BlindBoxView appeared with mediaType: \(mediaType)")
|
print("🎯 BlindBoxView appeared with mediaType: \(mediaType)")
|
||||||
print("🎯 Current thread: \(Thread.current)")
|
print("🎯 Current thread: \(Thread.current)")
|
||||||
|
|
||||||
@ -269,9 +183,9 @@ struct BlindBoxView: View {
|
|||||||
viewModel.stopCountdown()
|
viewModel.stopCountdown()
|
||||||
|
|
||||||
// Clean up video player
|
// Clean up video player
|
||||||
videoPlayer?.pause()
|
viewModel.player?.pause()
|
||||||
videoPlayer?.replaceCurrentItem(with: nil)
|
viewModel.player?.replaceCurrentItem(with: nil)
|
||||||
videoPlayer = nil
|
viewModel.player = nil
|
||||||
|
|
||||||
NotificationCenter.default.removeObserver(
|
NotificationCenter.default.removeObserver(
|
||||||
self,
|
self,
|
||||||
@ -282,8 +196,10 @@ struct BlindBoxView: View {
|
|||||||
.onChange(of: viewModel.blindGenerate?.status) { status in
|
.onChange(of: viewModel.blindGenerate?.status) { status in
|
||||||
guard let status = status?.lowercased() else { return }
|
guard let status = status?.lowercased() else { return }
|
||||||
if status == "unopened" {
|
if status == "unopened" {
|
||||||
|
Perf.event("BlindBox_Status_Unopened")
|
||||||
withAnimation { self.animationPhase = .ready }
|
withAnimation { self.animationPhase = .ready }
|
||||||
} else if status == "preparing" {
|
} else if status == "preparing" {
|
||||||
|
Perf.event("BlindBox_Status_Preparing")
|
||||||
withAnimation { self.animationPhase = .loading }
|
withAnimation { self.animationPhase = .loading }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,14 +239,14 @@ struct BlindBoxView: View {
|
|||||||
.edgesIgnoringSafeArea(.all)
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
if mediaType == .all, let player = videoPlayer {
|
if mediaType == .all, viewModel.player != nil {
|
||||||
// Video Player
|
// Video Player
|
||||||
AVPlayerController(player: $videoPlayer)
|
AVPlayerController(player: .init(get: { viewModel.player }, set: { viewModel.player = $0 }))
|
||||||
.frame(width: scaledWidth, height: scaledHeight)
|
.frame(width: scaledWidth, height: scaledHeight)
|
||||||
.opacity(scale == 1 ? 1 : 0.7)
|
.opacity(scale == 1 ? 1 : 0.7)
|
||||||
.onAppear { player.play() }
|
.onAppear { viewModel.player?.play() }
|
||||||
|
|
||||||
} else if mediaType == .image, let image = displayImage {
|
} else if mediaType == .image, let image = viewModel.displayImage {
|
||||||
// Image View
|
// Image View
|
||||||
Image(uiImage: image)
|
Image(uiImage: image)
|
||||||
.resizable()
|
.resizable()
|
||||||
@ -353,7 +269,7 @@ struct BlindBoxView: View {
|
|||||||
// 导航到BlindOutcomeView
|
// 导航到BlindOutcomeView
|
||||||
if mediaType == .all, !viewModel.videoURL.isEmpty, let url = URL(string: viewModel.videoURL) {
|
if mediaType == .all, !viewModel.videoURL.isEmpty, let url = URL(string: viewModel.videoURL) {
|
||||||
Router.shared.navigate(to: .blindOutcome(media: .video(url, nil), time: viewModel.blindGenerate?.name ?? "Your box", description:viewModel.blindGenerate?.description ?? "", isMember: viewModel.isMember))
|
Router.shared.navigate(to: .blindOutcome(media: .video(url, nil), time: viewModel.blindGenerate?.name ?? "Your box", description:viewModel.blindGenerate?.description ?? "", isMember: viewModel.isMember))
|
||||||
} else if mediaType == .image, let image = displayImage {
|
} else if mediaType == .image, let image = viewModel.displayImage {
|
||||||
Router.shared.navigate(to: .blindOutcome(media: .image(image), time: viewModel.blindGenerate?.name ?? "Your box", description:viewModel.blindGenerate?.description ?? "", isMember: viewModel.isMember))
|
Router.shared.navigate(to: .blindOutcome(media: .image(image), time: viewModel.blindGenerate?.name ?? "Your box", description:viewModel.blindGenerate?.description ?? "", isMember: viewModel.isMember))
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@ -514,6 +430,7 @@ struct BlindBoxView: View {
|
|||||||
.contentShape(Rectangle()) // Make the entire area tappable
|
.contentShape(Rectangle()) // Make the entire area tappable
|
||||||
.frame(width: 300, height: 300)
|
.frame(width: 300, height: 300)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
Perf.event("BlindBox_Open_Tapped")
|
||||||
print("点击了盲盒")
|
print("点击了盲盒")
|
||||||
|
|
||||||
let boxIdToOpen = self.currentBoxId ?? self.viewModel.blindGenerate?.id
|
let boxIdToOpen = self.currentBoxId ?? self.viewModel.blindGenerate?.id
|
||||||
@ -544,6 +461,7 @@ struct BlindBoxView: View {
|
|||||||
// 当显示媒体时,移除 GIFView 避免后台播放
|
// 当显示媒体时,移除 GIFView 避免后台播放
|
||||||
Color.clear
|
Color.clear
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
Perf.event("BlindBox_Opening_Begin")
|
||||||
print("开始播放开启动画")
|
print("开始播放开启动画")
|
||||||
// 初始缩放为1(原始大小)
|
// 初始缩放为1(原始大小)
|
||||||
self.scale = 1.0
|
self.scale = 1.0
|
||||||
@ -563,12 +481,9 @@ struct BlindBoxView: View {
|
|||||||
self.scale = 1.0
|
self.scale = 1.0
|
||||||
|
|
||||||
// 显示媒体内容
|
// 显示媒体内容
|
||||||
|
Perf.event("BlindBox_Opening_ShowMedia")
|
||||||
self.showScalingOverlay = true
|
self.showScalingOverlay = true
|
||||||
if mediaType == .all {
|
Task { await viewModel.prepareMedia() }
|
||||||
loadVideo()
|
|
||||||
} else if mediaType == .image {
|
|
||||||
loadImage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记显示媒体,隐藏GIF
|
// 标记显示媒体,隐藏GIF
|
||||||
self.showMedia = true
|
self.showMedia = true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user