下面的简单代码通过更新
@State var offset
块内的 withAnimation
来移动文本。
struct ContentView: View {
@State var offset: CGFloat = 0
var body: some View {
NavigationView {
VStack {
Text("Some moving text")
.offset(y: offset)
Button("Change Offset") {
withAnimation(.easeInOut(duration: 1.0)) {
offset = .random(in: -400...400)
}
}
}
}
}
}
在动画运行时是否可以以某种方式访问动画属性,例如当前值/偏移和速度?
例如,我想根据动画的当前速度向
Text
添加模糊:动画越快,模糊越强/模糊半径越大。
这可能吗?
考虑到用例,速度/速率并不重要,因为我不认为精确地尊重它会引人注目,并且开发一种基于观察或计算的速度来实现预期效果的方法可能有点矫枉过正。
(可变)速度实际上是由与所选动画相关的时序曲线给出的 -
.easeInOut
。但更重要的是动画持续时间。无论对象/文本移动的距离如何,此持续时间都是相同的。
如果持续时间相同,则意味着使用相同动画和持续时间更改/动画的任何值都将:
考虑到这一点,如果我们知道文本从当前偏移量移动到新偏移量需要一秒钟,则应用于它的任何模糊都应该:
实现此目的的最简单方法是将模糊值从零到适当值的变化动画化:
这可以通过使用相同(或相似)动画但持续时间值一半的模糊值来轻松完成,然后再次制作动画以在初始动画完成时重置该值:
withAnimation(.easeIn(duration: animationDuration / 2 )) {
blur = distanceBasedBlur
} completion: {
withAnimation(.easeOut(duration: animationDuration / 2 )) {
blur = 0
}
}
这是完整的代码,其中包含一些注释,希望能够解释一切:
import SwiftUI
struct AnimationBlur: View {
//Constants
let animationDuration: TimeInterval = 1.0
let offsetMinLimit: CGFloat = -400
let offsetMaxLimit: CGFloat = 400
let maxBlur: CGFloat = 10
//Computed properties
var maxDistance: CGFloat {
abs(offsetMaxLimit - offsetMinLimit)
}
//State values
@State private var targetOffset: CGFloat = 0
@State private var blur: CGFloat = 0
//Body
var body: some View {
VStack {
//Text
Text("Some moving text")
.blur(radius: blur)
.offset(y: targetOffset)
//Button
Button("Change Offset") {
withAnimation(.easeInOut(duration: animationDuration)) {
targetOffset = .random(in: offsetMinLimit...offsetMaxLimit)
}
}
}
.onChange(of: targetOffset) { currentOffset, targetOffset in
//Calculate a blur value based on distance, where maxBlur is achieved if travelling the max possible distance
let distanceBasedBlur = abs(targetOffset - currentOffset).rounded() / (maxDistance/maxBlur)
//Animate the change in blur based on time, such that relative blur max value is reached in half the time it takes for the text to move
withAnimation(.easeIn(duration: animationDuration / 2 )) {
blur = distanceBasedBlur
} completion: {
//When the animation ends, animate the text blur back to zero for the remaining animation duration
withAnimation(.easeOut(duration: animationDuration / 2 )) {
blur = 0
}
}
}
}
}
#Preview {
AnimationBlur()
}