复选标记动画问题

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

我正在使用以下代码来显示带有圆圈和复选标记的成功

struct CheckMarkView: View {
  @State var borderInit: Bool = false
  @State var spinArrow: Bool = false
  @State var dismissArrow: Bool = false
  @State var displayCheckmark: Bool = false
  
  var body: some View {
      ZStack {
          Circle()
              .strokeBorder(style: StrokeStyle(lineWidth: borderInit ? 10 : 64))
              .frame(width: 128, height: 128)
              .foregroundColor(borderInit ? .white : .black)
              .animation(.easeOut(duration: 3).speed(1.5))
              .onAppear() {
                  borderInit.toggle()
              }
          
          // Arrow Icon
          Image(systemName: "arrow.2.circlepath")
              .frame(width: 80, height: 80)
              .font(.largeTitle)
              .foregroundColor(.white)
              .rotationEffect(.degrees(spinArrow ? 360 : -360))
              .opacity(dismissArrow ? 0 : 1)
              .animation(.easeOut(duration: 2))
              .onAppear() {
                  spinArrow.toggle()
                  withAnimation(Animation.easeInOut(duration: 1).delay(1)) {
                      self.dismissArrow.toggle()
                  }
              }
          
          // Checkmark
         Path { path in
              path.move(to: CGPoint(x: 20, y: -40))
              path.addLine(to: CGPoint(x: 40, y: -20))
              path.addLine(to: CGPoint(x: 80, y: -60))
          }
          .trim(from: 0, to: displayCheckmark ? 1 : 0)
          .stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
          .foregroundColor(displayCheckmark ? .white : .black)
          .offset(x: 150, y: 420)
          // .animation(.spring(.bouncy, blendDuration: 2).delay(2))
          .animation(Animation.interpolatingSpring(stiffness: 150, damping: 10, initialVelocity: 0).delay(2))
          //  .animation(Animation.interpolatingSpring(mass: 1, stiffness: 100, damping: 10, initialVelocity: 0).delay(2))
          .onAppear() {
              displayCheckmark.toggle()
          }

    }

    .background(.black)
  }

}


#Preview {
    CheckMarkView()
        .frame(width: 128, height: 128)

}

当我尝试向 CheckMarkView 添加框架时,图像和圆圈显示在不同的位置。复选标记应显示在圆圈内。另外复选标记动画最后也不平滑。如何解决这个问题?任何帮助将不胜感激。

swift animation swiftui
1个回答
1
投票

您应该将复选标记打入

Shape
。这样,SwiftUI 就会为您处理布局,您无需使用
offset
将其放置在正确的位置。

这是一个

Shape
实施示例,基于您的
Path

中的要点
struct CheckmarkShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let fifthWidth = rect.width / 5
        let quarterHeight = rect.height / 4
        path.move(to: .init(x: rect.minX + fifthWidth, y: rect.minY + 2 * quarterHeight))
        path.addLine(to: .init(x: rect.minX + 2 * fifthWidth, y: rect.minY + 3 * quarterHeight))
        path.addLine(to: .init(x: rect.minX + 4 * fifthWidth, y: rect.minY + quarterHeight))
        return path
    }
}

CheckmarkView
看起来像这样:

@State var borderInit: Bool = false
@State var spinArrow: Bool = false
@State var dismissArrow: Bool = false
@State var displayCheckmark: Bool = false

var body: some View {
    ZStack {
        Color.black.ignoresSafeArea()
        Circle()
            .strokeBorder(style: StrokeStyle(lineWidth: borderInit ? 10 : 64))
            .frame(width: 128, height: 128)
            .foregroundColor(borderInit ? .white : .black)
            .animation(.easeOut(duration: 3).speed(1.5), value: borderInit)
            .onAppear() {
                borderInit.toggle()
            }
        
        // Arrow Icon
        Image(systemName: "arrow.2.circlepath")
            .frame(width: 80, height: 80)
            .font(.largeTitle)
            .foregroundColor(.white)
            .rotationEffect(.degrees(spinArrow ? 360 : -360))
            .opacity(dismissArrow ? 0 : 1)
            .animation(.easeOut(duration: 2), value: dismissArrow)
            .animation(.easeOut(duration: 2), value: spinArrow)
            .onAppear() {
                spinArrow.toggle()
                withAnimation(Animation.easeInOut(duration: 1).delay(1)) {
                    self.dismissArrow.toggle()
                }
            }
        CheckmarkShape()
            .trim(from: 0, to: displayCheckmark ? 1 : 0)
            .stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
            .frame(width: 100, height: 100)
            .foregroundColor(displayCheckmark ? .white : .black)
            .animation(.bouncy.delay(2), value: displayCheckmark)
            .onAppear() {
                displayCheckmark.toggle()
            }
    }
}

您应该将

value
参数传递给所有
animation
修饰符。不推荐使用没有
value
的版本。

由于您使用了弹簧动画,我假设您希望复选标记的笔划稍微超出它应该结束的位置,然后弹回来。您可以通过在

Shape
实现中扩展箭头的路径并将
trim
设置为小于 1 的动画来实现此目的。

struct CheckmarkShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let fifthWidth = rect.width / 5
        let quarterHeight = rect.height / 4
        path.move(to: .init(x: rect.minX + fifthWidth, y: rect.minY + 2 * quarterHeight))
        path.addLine(to: .init(x: rect.minX + 2 * fifthWidth, y: rect.minY + 3 * quarterHeight))
        path.addLine(to: .init(x: rect.minX + 5 * fifthWidth, y: rect.minY))
        return path
    }
}
.trim(from: 0, to: displayCheckmark ? 0.75 : 0)
© www.soinside.com 2019 - 2024. All rights reserved.