面试题答案
一键面试实现思路
- 获取滚动距离:使用
ScrollViewReader
来获取ScrollView
的滚动位置,以便根据滚动距离来控制子视图的动画。 - 计算动画参数:根据滚动距离计算每个子视图的缩放比例、旋转角度和透明度。可以通过定义一些函数或者公式来实现。
- 应用动画:在
ForEach
循环遍历Stack
中的子视图时,根据计算出的动画参数为每个子视图应用动画效果。
关键技术点
- ScrollViewReader:用于获取
ScrollView
的滚动位置。 - GeometryReader:可以获取视图的几何信息,用于结合滚动距离计算动画参数。
- AnimatableModifier:用于创建自定义的动画效果,可对视图的属性(如缩放、旋转、透明度)进行动画处理。
核心代码实现
import SwiftUI
struct CustomAnimationView: View {
@State private var scrollOffset: CGFloat = 0
var body: some View {
ScrollView {
ScrollViewReader { proxy in
VStack {
ForEach(0..<10) { index in
Text("Item \(index)")
.modifier(
CustomAnimationModifier(
scrollOffset: scrollOffset,
itemIndex: index
)
)
}
}
.onChange(of: proxy.contentOffset.y) { newValue in
scrollOffset = newValue
}
}
}
}
}
struct CustomAnimationModifier: AnimatableModifier {
var scrollOffset: CGFloat
var itemIndex: Int
var animatableData: CGFloat {
get { scrollOffset }
set { scrollOffset = newValue }
}
func body(content: Content) -> some View {
let distance = scrollOffset - CGFloat(itemIndex * 100)
let scale = max(0.5, 1 - abs(distance) / 200)
let rotation = min(45, abs(distance) / 5).sign * angle
let opacity = max(0.2, 1 - abs(distance) / 150)
return content
.scaleEffect(scale)
.rotationEffect(rotation)
.opacity(opacity)
}
private var angle: Angle {
Angle(degrees: 1)
}
}
在上述代码中:
CustomAnimationView
使用ScrollViewReader
获取滚动偏移量scrollOffset
。CustomAnimationModifier
根据滚动偏移量scrollOffset
和子视图的索引itemIndex
计算缩放比例scale
、旋转角度rotation
和透明度opacity
,并应用到子视图上。
起始、过程和结束状态设置
- 起始状态:当
ScrollView
尚未滚动时,子视图处于原始状态,缩放比例为1,旋转角度为0,透明度为1。 - 过程状态:随着
ScrollView
滚动,scrollOffset
值发生变化,CustomAnimationModifier
根据新的scrollOffset
值重新计算缩放、旋转和透明度,子视图以动画形式过渡到新状态。 - 结束状态:当
ScrollView
停止滚动,子视图保持当前根据scrollOffset
计算出的缩放、旋转和透明度状态。如果滚动到某个子视图完全不可见的位置,根据计算逻辑,其透明度可能会降低到最小值(如0.2),缩放比例也可能缩小到最小值(如0.5)。