getAnimations method

  1. @override
List<CharacterAnimation> getAnimations(
  1. double progress,
  2. int charCount
)
override

Produces a list of per-character animations for the given progress.

progress ranges from 0.0 (start) to 1.0 (end). The returned list must have exactly charCount elements.

Implementation

@override
List<CharacterAnimation> getAnimations(double progress, int charCount) {
  if (charCount <= 1) return List.filled(charCount, const CharacterAnimation());
  final curved = applyCurve(progress);

  // Generate drop positions along [0, 1] using deterministic hashing.
  final drops = List.generate(dropCount, (i) {
    final h = ((seed + i * 7919) * 374761393) & 0x7FFFFFFF;
    return (h % 10000) / 10000.0;
  });

  final charPositions = List.generate(charCount, (i) => charCount > 1 ? i / (charCount - 1) : 0.5);

  return List.generate(charCount, (index) {
    final charPos = charPositions[index];

    // Find distance to nearest drop center.
    var minDist = double.infinity;
    for (final drop in drops) {
      final dist = (charPos - drop).abs();
      if (dist < minDist) minDist = dist;
    }

    // Normalize distance: 0 at drop, 1 at spreadDistance away.
    final normalizedDist = (minDist / (spreadDistance / (charCount - 1))).clamp(0.0, 1.0);

    // Character is revealed when curved progress exceeds its distance threshold.
    final revealT = ((curved - normalizedDist) / (1.0 - normalizedDist)).clamp(0.0, 1.0);

    // Scale: ink splatter starts small and grows to full size.
    final scale = 0.3 + 0.7 * revealT;
    // Opacity: fully opaque after reveal.
    final opacity = revealT;

    return CharacterAnimation(
      opacity: opacity.clamp(0.0, 1.0),
      scale: scale,
      blurSigma: (1.0 - revealT) * 2.0,
    );
  });
}