在 SwiftUI 中使用比例和不透明度制作动画

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

如何以 0 到 1 的比例对圆形图像进行动画处理,同时沿圆的圆周以 7 点钟的起始位置和 11 点钟的缩放结束位置进行动画处理,然后在 11 时以从 1 到 0 的不透明度进行动画处理点到三点。然后将图像重新定位回 7 点钟位置并循环播放动画。使用以下代码,我可以同时缩放和旋转,但它不会沿着圆周移动。我不知道如何在缩放和旋转过程中偏移动画,使其保持沿着圆圈。

struct ContentView: View {
    @State private var startAngle: Double = -45
    @State private var endAngle: Double = 120
    @State private var animationValue: Double = 0.0

    var body: some View {
        ZStack {
            let width: CGFloat = 60
            Circle()
                .stroke(.black, lineWidth: 1.0)
                .frame(width: width)
                .overlay {
                    Image(systemName: "circle.fill")
                        .offset(x: -width/2)
                        .modifier(ScaleAndRotateModifier(value: animationValue,
                                                         startAngle: startAngle,
                                                         endAngle: endAngle))
                        .onAppear {
                            withAnimation(.easeInOut(duration: 1.0).repeatForever(autoreverses: true)) {
                                animationValue = 1.0
                            }
                        }
                }
        }
        .foregroundStyle(.black)
        .padding()
    }
}

struct ScaleAndRotateModifier: AnimatableModifier {
    var value: Double
    var startAngle: Double
    var endAngle: Double
    
    var animatableData: Double {
        get { value }
        set { value = newValue }
    }
    
    func body(content: Content) -> some View {
        content
            .rotationEffect(Angle(degrees: startAngle + value * endAngle), anchor: .center)
            .scaleEffect(value * 0.5)
    }
}
ios animation swiftui
1个回答
0
投票

由于这个动画涉及多个阶段,所以这里使用

keyframeAnimator
会很方便。

编写一个包含您想要设置动画的所有属性的结构:

struct AnimatedProperties {
    var angle: Angle = .degrees(120)
    var scale: CGFloat = 0
    var opacity: CGFloat = 1
}

那么你可以做

let width: CGFloat = 60
Circle()
    .stroke(.black, lineWidth: 1.0)
    .frame(width: width)
    .overlay {
        Image(systemName: "circle.fill")
            .keyframeAnimator(initialValue: AnimatedProperties()) { content, properties in
                content
                    .scaleEffect(properties.scale)
                    .opacity(properties.opacity)
                    .offset(x: width / 2 * cos(properties.angle.radians), y: width / 2 * sin(properties.angle.radians))
            } keyframes: { properties in
                KeyframeTrack(\.angle) {
                    LinearKeyframe(.degrees(360), duration: 2)
                }
                KeyframeTrack(\.scale) {
                    LinearKeyframe(1, duration: 1)
                }
                KeyframeTrack(\.opacity) {
                    LinearKeyframe(1, duration: 1)
                    LinearKeyframe(0, duration: 1)
                }
            }

    }

注意这一行:

.offset(x: width / 2 * cos(properties.angle.radians), y: width / 2 * sin(properties.angle.radians))

这就是计算给定角度所需偏移的方法。

这里我假设您希望圆圈立即跳回其起始位置。如果您也想将这部分动画设置为持续 1 秒,请将

\.angle
轨道更改为一直到 480 度,并持续 3 秒。

KeyframeTrack(\.angle) {
    LinearKeyframe(.degrees(480), duration: 3)
}
© www.soinside.com 2019 - 2024. All rights reserved.