feat: 动画后视频

This commit is contained in:
jinyaqiu 2025-09-01 16:25:03 +08:00
parent 34b6fa7894
commit 08a82386da
3 changed files with 104 additions and 14 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

@ -501,6 +501,7 @@ struct BlindBoxView: View {
self.displayImage = image self.displayImage = image
self.aspectRatio = image.size.width / image.size.height self.aspectRatio = image.size.width / image.size.height
self.isPortrait = image.size.height > image.size.width self.isPortrait = image.size.height > image.size.width
self.showScalingOverlay = true //
} }
} }
}.resume() }.resume()
@ -526,11 +527,53 @@ struct BlindBoxView: View {
isPortrait = height > width isPortrait = height > width
} }
// Update the video player //
videoPlayer = player videoPlayer = player
videoPlayer?.play() videoPlayer?.play()
showScalingOverlay = true //
} }
private func prepareVideo() {
guard !videoURL.isEmpty, let url = URL(string: 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 !imageURL.isEmpty, let url = URL(string: 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
self.showScalingOverlay = true self.showScalingOverlay = true
@ -822,15 +865,30 @@ struct BlindBoxView: View {
.frame(width: 300, height: 300) .frame(width: 300, height: 300)
case .opening: case .opening:
GIFView(name: "BlindOpen") ZStack {
.frame(width: 300, height: 300) GIFView(name: "BlindOpen")
.onAppear { .frame(width: 300, height: 300)
self.loadMedia() .onAppear {
// Start animation after media is loaded print("开始播放开启动画")
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { //
self.startScalingAnimation()
// 2.5
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
//
if mediaType == .video {
loadVideo()
} else if mediaType == .image {
loadImage()
}
//
withAnimation(.easeInOut(duration: 0.5)) {
self.startScalingAnimation()
}
}
} }
} }
.frame(width: 300, height: 300)
case .none: case .none:
SVGImage(svgName: "BlindNone") SVGImage(svgName: "BlindNone")

View File

@ -162,6 +162,12 @@ struct BlindOutcomeView: View {
} }
.padding(.bottom, 20) .padding(.bottom, 20)
} }
.onDisappear {
// Clean up video player when view disappears
if case .video = media {
isPlaying = false
}
}
} }
.navigationBarHidden(true) // .navigationBarHidden(true) //
.navigationBarBackButtonHidden(true) // .navigationBarBackButtonHidden(true) //
@ -169,6 +175,9 @@ struct BlindOutcomeView: View {
} }
.navigationViewStyle(StackNavigationViewStyle()) // iPad .navigationViewStyle(StackNavigationViewStyle()) // iPad
.navigationBarHidden(true) // .navigationBarHidden(true) //
.overlay(
JoinModal(isPresented: $showIPListModal)
)
} }
} }
@ -293,14 +302,18 @@ struct VideoPlayerView: UIViewRepresentable {
class PlayerView: UIView { class PlayerView: UIView {
private var player: AVPlayer? private var player: AVPlayer?
private var playerLayer: AVPlayerLayer? private var playerLayer: AVPlayerLayer?
private var playerItem: AVPlayerItem?
private var playerItemObserver: NSKeyValueObservation?
func setupPlayer(url: URL) { func setupPlayer(url: URL) {
// Remove existing player if any // Clean up existing resources
playerLayer?.removeFromSuperlayer() cleanup()
// Create new player // Create new player
let asset = AVAsset(url: url) let asset = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: asset) let playerItem = AVPlayerItem(asset: asset)
self.playerItem = playerItem
player = AVPlayer(playerItem: playerItem) player = AVPlayer(playerItem: playerItem)
// Setup player layer // Setup player layer
@ -329,6 +342,27 @@ class PlayerView: UIView {
player?.pause() player?.pause()
} }
private func cleanup() {
// Remove observers
if let playerItem = playerItem {
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: playerItem)
}
// Pause and clean up player
player?.pause()
player?.replaceCurrentItem(with: nil)
player = nil
// Remove player layer
playerLayer?.removeFromSuperlayer()
playerLayer = nil
// Release player item
playerItem?.cancelPendingSeeks()
playerItem?.asset.cancelLoading()
playerItem = nil
}
@objc private func playerItemDidReachEnd() { @objc private func playerItemDidReachEnd() {
player?.seek(to: .zero) player?.seek(to: .zero)
player?.play() player?.play()
@ -340,9 +374,7 @@ class PlayerView: UIView {
} }
deinit { deinit {
player?.pause() cleanup()
player?.replaceCurrentItem(with: nil)
NotificationCenter.default.removeObserver(self)
} }
} }