feat: 动画后视频
This commit is contained in:
parent
34b6fa7894
commit
08a82386da
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 MiB After Width: | Height: | Size: 2.8 MiB |
@ -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")
|
||||||
|
|||||||
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user