Анимация букв в слове при открытии приложения - SwiftUI - PullRequest
1 голос
/ 18 февраля 2020

Я пытаюсь получить эффект танцующих букв при первом открытии моего приложения.

Я близок. Код ниже почти делает то, что я хочу. Я использую ForEach от l oop до l oop через буквы слова и применяю анимацию к каждой букве. И я использую функцию onAppear, чтобы установить величину перетаскивания при открытии приложения.

С помощью этой кодировки я могу получить движение вперед, но не могу заставить анимацию повернуть вспять, чтобы буквы оказались в их исходное положение. Я попытался добавить повтор с реверсом, но, опять же, буквы никогда не возвращаются в исходное положение

Кто-нибудь знает, как это сделать?

struct ContentView: View {
  let letters = Array("Math Fun!")
  @State private var enabled = false
  @State private var dragAmount = CGSize.zero

  var body: some View {
    HStack(spacing: 0) {
      ForEach(0..<letters.count) { num in
        Text(String(self.letters[num]))
          .padding(5)
          .font(.title)
          .background(self.enabled ? Color.blue : Color.red)
          .offset(self.dragAmount)
          .animation(Animation.default.delay(Double(num)/20).repeatCount(3, autoreverses: true))
      }
    }
    .onAppear {
      self.dragAmount = CGSize(width: 0, height: 80)
      self.enabled.toggle()
    }
  }

}

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Вы можете использовать AnimatableModifier для достижения этого эффекта. Вот пример кода:

extension Double {
    var rad: Double { return self * .pi / 180 }
    var deg: Double { return self * 180 / .pi }
}

struct ContentView: View {
    @State private var flag = false

    var body: some View {
        VStack {
            Spacer()
            Color.clear.overlay(WaveText("Your Text That Need Animate", waveWidth: 6, pct: flag ? 1.0 : 0.0).foregroundColor(.blue)).frame(height: 40)
            Spacer()
        }.onAppear {
            withAnimation(Animation.easeInOut(duration: 2.0).repeatForever()) {
                self.flag.toggle()
            }
        }
    }
}

struct WaveText: View {
    let text: String
    let pct: Double
    let waveWidth: Int
    var size: CGFloat

    init(_ text: String, waveWidth: Int, pct: Double, size: CGFloat = 34) {
        self.text = text
        self.waveWidth = waveWidth
        self.pct = pct
        self.size = size
    }

    var body: some View {
        Text(text).foregroundColor(Color.clear).modifier(WaveTextModifier(text: text, waveWidth: waveWidth, pct: pct, size: size))
    }

    struct WaveTextModifier: AnimatableModifier {
        let text: String
        let waveWidth: Int
        var pct: Double
        var size: CGFloat

        var animatableData: Double {
            get { pct }
            set { pct = newValue }
        }

        func body(content: Content) -> some View {

            HStack(spacing: 0) {
                ForEach(Array(text.enumerated()), id: \.0) { (n, ch) in
                    Text(String(ch))
                        .scaleEffect(self.effect(self.pct, n, self.text.count, Double(self.waveWidth)))
                }
            }
        }

        func effect(_ pct: Double, _ n: Int, _ total: Int, _ waveWidth: Double) -> CGFloat {
            let n = Double(n)
            let total = Double(total)

            return CGFloat(1 + valueInCurve(pct: pct, total: total, x: n/total, waveWidth: waveWidth))
        }

        func valueInCurve(pct: Double, total: Double, x: Double, waveWidth: Double) -> Double {
            let chunk = waveWidth / total
            let m = 1 / chunk
            let offset = (chunk - (1 / total)) * pct
            let lowerLimit = (pct - chunk) + offset
            let upperLimit = (pct) + offset

            guard x >= lowerLimit && x < upperLimit else { return 0 }

            let angle = ((x - pct - offset) * m)*360-90

            return (sin(angle.rad) + 1) / 2
        }
    }
}

Здесь вы можете найти референс и ответ участника здесь

1 голос
/ 18 февраля 2020

Анимация основана на измененных состояниях, вы переключили состояния и просматриваете анимированные в новое состояние, поэтому для отката необходимо переключить состояния обратно.

Вот возможный подход (может потребоваться настройка, но все в порядке для демонстрации)

enter image description here

struct ContentView: View {
  let letters = Array("Math Fun!")
  @State private var enabled = false
  @State private var dragAmount = CGSize.zero

  var body: some View {
    HStack(spacing: 0) {
      ForEach(0..<letters.count) { num in
        Text(String(self.letters[num]))
          .padding(5)
          .font(.title)
          .background(self.enabled ? Color.blue : Color.red)
          .offset(self.dragAmount)
        .animation(Animation.default.delay(Double(num)/20))
      }
    }
    .onAppear {
      self.dragAmount = CGSize(width: 0, height: 80)
      self.enabled.toggle()
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.dragAmount = .zero
            self.enabled.toggle()
        }
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...