// // ScenicPromotionMediaArtworkView.swift // suixinkan // // Created by Codex on 2026/7/2. // import SwiftUI /// 宣传页素材图片渲染组件,支持缩略图与主视觉两种尺寸。 struct ScenicPromotionMediaArtwork: View { let media: ScenicPromotionMedia let compact: Bool let useHeroAsset: Bool var body: some View { GeometryReader { proxy in if let assetName = useHeroAsset ? (media.heroAssetName ?? media.assetName) : media.assetName { Image(assetName) .resizable() .scaledToFill() .frame(width: proxy.size.width, height: proxy.size.height) .clipped() } else { ZStack { LinearGradient( colors: media.palette.map { Color(hex: $0) }, startPoint: .topLeading, endPoint: .bottomTrailing ) ScenicPromotionMountainLayer() .fill(.white.opacity(0.28)) .frame(height: proxy.size.height * 0.45) .offset(y: proxy.size.height * 0.02) ScenicPromotionMountainLayer() .fill(Color(hex: 0x0F5B85, alpha: 0.22)) .frame(height: proxy.size.height * 0.38) .offset(y: proxy.size.height * 0.13) VStack { Spacer() Rectangle() .fill(Color(hex: 0x2D9D4E, alpha: 0.52)) .frame(height: proxy.size.height * 0.2) } if !compact { Image(systemName: "figure.walk") .font(.system(size: 74, weight: .bold)) .foregroundStyle(.white.opacity(0.82)) .offset(x: proxy.size.width * 0.23, y: proxy.size.height * 0.1) } } } } .clipShape(RoundedRectangle(cornerRadius: compact ? 12 : 18)) } } /// 没有本地图片资源时使用的备用山形占位图。 struct ScenicPromotionMountainLayer: Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) path.addLine(to: CGPoint(x: rect.width * 0.16, y: rect.height * 0.42)) path.addLine(to: CGPoint(x: rect.width * 0.28, y: rect.height * 0.68)) path.addLine(to: CGPoint(x: rect.width * 0.42, y: rect.height * 0.25)) path.addLine(to: CGPoint(x: rect.width * 0.58, y: rect.height * 0.6)) path.addLine(to: CGPoint(x: rect.width * 0.76, y: rect.height * 0.18)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.height * 0.56)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) path.closeSubpath() return path } } #if DEBUG #Preview("素材缩略图") { ScenicPromotionMediaArtwork( media: ScenicPromotionPreviewSupport.sampleVideoMedia, compact: true, useHeroAsset: false ) .frame(width: 72, height: 66) .padding() } #Preview("素材主视觉") { ScenicPromotionMediaArtwork( media: ScenicPromotionPreviewSupport.sampleVideoMedia, compact: false, useHeroAsset: true ) .frame(maxWidth: .infinity) .frame(height: 220) .padding() } #endif