fix: 修复裁剪手势状态丢失 + 合并重复预览代码
- 为 MagnificationGesture/DragGesture 添加 .onEnded 处理器, 新增 lastCropOffset/lastCropScale 累积变量,修复抬手后 缩放/偏移从初始值重新开始的问题 - 提取 cropPreview(height:dynamicHeight:) 共享方法和 cropGesture 计算属性,消除 iPhone/iPad 约 60 行重复代码 - resetCropState() 同步重置累积变量 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,8 @@ struct EditorView: View {
|
||||
// 裁剪相关(归一化坐标)
|
||||
@State private var cropOffset: CGSize = .zero // 拖拽偏移
|
||||
@State private var cropScale: CGFloat = 1.0 // 缩放比例
|
||||
@State private var lastCropOffset: CGSize = .zero // 累积偏移
|
||||
@State private var lastCropScale: CGFloat = 1.0 // 累积缩放
|
||||
|
||||
// 兼容模式
|
||||
@State private var compatibilityMode: Bool = false
|
||||
@@ -72,7 +74,7 @@ struct EditorView: View {
|
||||
private var iPhoneLayout: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 20) {
|
||||
cropPreviewSection
|
||||
cropPreview(height: 360)
|
||||
|
||||
if let diagnosis = videoDiagnosis, !diagnosis.suggestions.isEmpty {
|
||||
diagnosisSection(diagnosis: diagnosis)
|
||||
@@ -97,7 +99,7 @@ struct EditorView: View {
|
||||
HStack(alignment: .top, spacing: 24) {
|
||||
// 左侧:视频预览
|
||||
VStack(spacing: 16) {
|
||||
iPadCropPreviewSection
|
||||
cropPreview(height: 500, dynamicHeight: true)
|
||||
|
||||
if let diagnosis = videoDiagnosis, !diagnosis.suggestions.isEmpty {
|
||||
diagnosisSection(diagnosis: diagnosis)
|
||||
@@ -124,80 +126,50 @@ struct EditorView: View {
|
||||
.padding(DesignTokens.Spacing.xxl)
|
||||
}
|
||||
|
||||
// MARK: - iPad 裁剪预览(更大尺寸)
|
||||
@ViewBuilder
|
||||
private var iPadCropPreviewSection: some View {
|
||||
GeometryReader { geometry in
|
||||
let containerWidth = geometry.size.width
|
||||
let containerHeight = min(500, geometry.size.width * 1.2)
|
||||
|
||||
ZStack {
|
||||
if let player {
|
||||
VideoPlayer(player: player)
|
||||
.aspectRatio(videoNaturalSize, contentMode: .fit)
|
||||
.scaleEffect(cropScale)
|
||||
.offset(cropOffset)
|
||||
.gesture(
|
||||
SimultaneousGesture(
|
||||
MagnificationGesture()
|
||||
.onChanged { value in
|
||||
cropScale = max(1.0, min(3.0, value))
|
||||
},
|
||||
DragGesture()
|
||||
.onChanged { value in
|
||||
cropOffset = value.translation
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
ProgressView()
|
||||
// MARK: - 裁剪手势
|
||||
private var cropGesture: some Gesture {
|
||||
SimultaneousGesture(
|
||||
MagnificationGesture()
|
||||
.onChanged { value in
|
||||
cropScale = max(1.0, min(3.0, lastCropScale * value))
|
||||
}
|
||||
|
||||
if selectedAspectRatio != .original {
|
||||
CropOverlay(
|
||||
aspectRatio: selectedAspectRatio,
|
||||
containerSize: CGSize(width: containerWidth, height: containerHeight)
|
||||
.onEnded { value in
|
||||
lastCropScale = max(1.0, min(3.0, lastCropScale * value))
|
||||
},
|
||||
DragGesture()
|
||||
.onChanged { value in
|
||||
cropOffset = CGSize(
|
||||
width: lastCropOffset.width + value.translation.width,
|
||||
height: lastCropOffset.height + value.translation.height
|
||||
)
|
||||
}
|
||||
}
|
||||
.frame(width: containerWidth, height: containerHeight)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
.background(Color.black.clipShape(RoundedRectangle(cornerRadius: 16)))
|
||||
}
|
||||
.frame(height: 500)
|
||||
.onEnded { value in
|
||||
lastCropOffset = CGSize(
|
||||
width: lastCropOffset.width + value.translation.width,
|
||||
height: lastCropOffset.height + value.translation.height
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - 裁剪预览
|
||||
// MARK: - 裁剪预览(共享)
|
||||
@ViewBuilder
|
||||
private var cropPreviewSection: some View {
|
||||
private func cropPreview(height: CGFloat, dynamicHeight: Bool = false) -> some View {
|
||||
GeometryReader { geometry in
|
||||
let containerWidth = geometry.size.width
|
||||
let containerHeight: CGFloat = 360
|
||||
let containerHeight = dynamicHeight ? min(height, geometry.size.width * 1.2) : height
|
||||
|
||||
ZStack {
|
||||
// 视频预览(全画幅)
|
||||
if let player {
|
||||
VideoPlayer(player: player)
|
||||
.aspectRatio(videoNaturalSize, contentMode: .fit)
|
||||
.scaleEffect(cropScale)
|
||||
.offset(cropOffset)
|
||||
.gesture(
|
||||
SimultaneousGesture(
|
||||
MagnificationGesture()
|
||||
.onChanged { value in
|
||||
cropScale = max(1.0, min(3.0, value))
|
||||
},
|
||||
DragGesture()
|
||||
.onChanged { value in
|
||||
cropOffset = value.translation
|
||||
}
|
||||
)
|
||||
)
|
||||
.gesture(cropGesture)
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
|
||||
// 裁剪框叠加层
|
||||
if selectedAspectRatio != .original {
|
||||
CropOverlay(
|
||||
aspectRatio: selectedAspectRatio,
|
||||
@@ -209,7 +181,7 @@ struct EditorView: View {
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
.background(Color.black.clipShape(RoundedRectangle(cornerRadius: 16)))
|
||||
}
|
||||
.frame(height: 360)
|
||||
.frame(height: height)
|
||||
}
|
||||
|
||||
// MARK: - 比例模板选择
|
||||
@@ -710,6 +682,8 @@ struct EditorView: View {
|
||||
private func resetCropState() {
|
||||
cropOffset = .zero
|
||||
cropScale = 1.0
|
||||
lastCropOffset = .zero
|
||||
lastCropScale = 1.0
|
||||
}
|
||||
|
||||
private func calculateCropRect() -> CropRect {
|
||||
|
||||
Reference in New Issue
Block a user