是否可以使用withAnimation块的动画属性(当前值、速度)来更改其他值?

问题描述 投票:0回答:1

下面的简单代码通过更新

@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
添加模糊:动画越快,模糊越强/模糊半径越大。

这可能吗?

ios swift xcode swiftui swiftui-animation
1个回答
0
投票

考虑到用例,速度/速率并不重要,因为我不认为精确地尊重它会引人注目,并且开发一种基于观察或计算的速度来实现预期效果的方法可能有点矫枉过正。

(可变)速度实际上是由与所选动画相关的时序曲线给出的 -

.easeInOut
。但更重要的是动画持续时间。无论对象/文本移动的距离如何,此持续时间都是相同的。

如果持续时间相同,则意味着使用相同动画和持续时间更改/动画的任何值都将:

  1. 同时启动和停止。
  2. 变化/动画的速率将是相同的(相同的时序曲线)。

考虑到这一点,如果我们知道文本从当前偏移量移动到新偏移量需要一秒钟,则应用于它的任何模糊都应该:

  1. 申请时间不超过一秒。
  2. 以一种感觉自然的方式重置为零(而不是在没有动画的情况下重置)。

实现此目的的最简单方法是将模糊值从零到适当值的变化动画化:

  1. 基于行进的距离(因为如果行进的距离很短,文本完全模糊会感觉不自然)
  2. 达到新偏移量只需一半时间
  3. 并在另一半期间恢复为零。

这可以通过使用相同(或相似)动画但持续时间值一半的模糊值来轻松完成,然后再次制作动画以在初始动画完成时重置该值:

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()
}
© www.soinside.com 2019 - 2024. All rights reserved.