以下是我想要实现的动画,当视图出现直到滚动视图滚动时,视图一个接一个地堆叠,当它们滚动时,它并排出现。
struct ContentView: View {
private let colors: [Color] = [.red, .green, .blue, .yellow, .purple, .black, .brown, .cyan, .gray, .indigo, .orange, .pink, .mint, .teal]
var body: some View {
VStack {
ScrollView(.horizontal) {
LazyHStack(spacing: 10.0) {
ForEach(colors, id: \.self) { color in
RoundedRectangle(cornerRadius: 25.0)
.foregroundStyle(color)
.containerRelativeFrame(.horizontal)
.scrollTransition { content, phase in
content
.opacity(phase.isIdentity ? 1: 0.2)
.scaleEffect(y: phase.isIdentity ? 1.0 : 0.8)
}
}
}
.scrollTargetLayout()
}
.contentMargins(60.0, for: .scrollContent)
.scrollTargetBehavior(.viewAligned)
.scrollIndicators(.hidden)
.frame(maxHeight: 400.0)
Spacer()
}
}
}
我能够并排获取视图,但如何将视图排列在后面,然后在滚动时平滑地过渡到并排。
为了实现这一点,您可以调整旋转值和项目之间的间距。您可以通过执行以下操作来做到这一点:
struct CoverFlowView<Content: View, Item: RandomAccessCollection>: View where Item.Element: Identifiable {
//MARK: - PROPERTIES
/// Customization
var itemWidth: CGFloat
var spacing: CGFloat = .zero
var rotation: CGFloat = .zero
var items: Item
var content: (Item.Element) -> Content
var body: some View {
GeometryReader {
let size = $0.size
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 0) {
ForEach(items) { item in
content(item)
.frame(width: itemWidth)
.visualEffect { content, geometryProxy in
content
.rotation3DEffect(.degrees(rotation(geometryProxy)),
axis: (x: 0, y: 1, z: 0),
anchor: .center)
}
.padding(.trailing, item.id == items.last?.id ? 0 : spacing)
} //: LOOP ITEMS
} //: LAZY HSTACK
.padding(.horizontal, (size.width - itemWidth) / 2)
.scrollTargetLayout()
} //: SCROLL
.scrollTargetBehavior(.viewAligned)
.scrollClipDisabled()
} //: GEOMETRY
}
//MARK: - Functions
func rotation(_ proxy: GeometryProxy) -> Double {
let scrollViewWidth = proxy.bounds(of: .scrollView(axis: .horizontal))?.width ?? 0
let midX = proxy.frame(in: .scrollView(axis: .horizontal)).midX
/// Converting into progress
let progress = midX / scrollViewWidth
/// Capping progress 0-1
let cappedProgress = max(min(progress, 1), 0)
/// Limit rotation between 0-90°
let cappedRotation = max(min(rotation, 90), 0)
let degree = cappedProgress * (cappedRotation * 2)
return cappedRotation - degree
}
}
你这样使用它:
/// State variables
@State private var spacing: CGFloat = 0
@State private var rotation: CGFloat = 0
@State private var mirrorCards = false
CoverFlowView(itemWidth: 250,
spacing: spacing,
rotation: rotation,
items: items) { item in
RoundedRectangle(cornerRadius: 20)
.fill(item.color.gradient)
}
.frame(height: 180)
.rotation3DEffect(
.degrees(mirrorCards ? 180 : 0),
axis: (x: 0.0, y: 1.0, z: 0.0)
)
我在这个简单的例子中使用的项目只是一个具有颜色属性的可识别结构,但您可以使用您真正喜欢的任何东西。 如果您使用大约 -220 的间距并镜像卡片,您可以获得与您正在寻找的结果类似的结果:
让我知道你是如何找到这个解决方案的!