layerAnimationProgress function

LayerAnimationProgress? layerAnimationProgress(
  1. LayerAnimation animation,
  2. Duration time, {
  3. required Duration? start,
  4. required Duration? end,
})

The progress of animation at time into the video, or null when it does not play then.

start and end are the layer's time range (null = the start / the end of the video): an in-animation plays over the first LayerAnimation.duration of it and an out-animation over the last. With AnimationPhase.animateInOut both apply and the one further from rest wins.

A AnimationPhase.loop plays over the whole range, one cycle per duration counted from start: the eased value runs from rest to fully away at half a cycle and back. A wiggle runs that twice per cycle, once to each side (see LayerAnimationProgress.swing).

Mirrors animationProgress in the native renderer of pro_video_editor, down to the whole microseconds the cycle position is taken from.

Implementation

LayerAnimationProgress? layerAnimationProgress(
  LayerAnimation animation,
  Duration time, {
  required Duration? start,
  required Duration? end,
}) {
  final durationUs = animation.duration.inMicroseconds;
  if (durationUs <= 0) return null;

  final startUs = (start ?? Duration.zero).inMicroseconds;
  final curve = animation.curve;

  if (animation.phase == AnimationPhase.loop) {
    final elapsed = math.max(0, time.inMicroseconds - startUs);
    final inCycle = elapsed % durationUs;
    if (animation.type == LayerAnimationType.wiggle) {
      // Each half of the cycle is one swing out and back.
      final inSwing = (2 * inCycle) % durationUs;
      final x = (1 - 2 * inSwing / durationUs).abs();
      return LayerAnimationProgress(
        applyLayerAnimationCurve(curve, x),
        swing: 2 * inCycle < durationUs ? 1 : -1,
      );
    }
    final x = (1 - 2 * inCycle / durationUs).abs();
    return LayerAnimationProgress(applyLayerAnimationCurve(curve, x));
  }

  double? inProgress;
  double? outProgress;

  if (animation.phase == AnimationPhase.animateIn ||
      animation.phase == AnimationPhase.animateInOut) {
    final elapsed = time.inMicroseconds - startUs;
    if (elapsed < durationUs) {
      inProgress = applyLayerAnimationCurve(
        curve,
        (elapsed / durationUs).clamp(0.0, 1.0),
      );
    }
  }

  if ((animation.phase == AnimationPhase.animateOut ||
          animation.phase == AnimationPhase.animateInOut) &&
      end != null) {
    final remaining = end.inMicroseconds - time.inMicroseconds;
    if (remaining < durationUs) {
      outProgress = applyLayerAnimationCurve(
        curve,
        (remaining / durationUs).clamp(0.0, 1.0),
      );
    }
  }

  // The animation further from rest wins.
  if (inProgress != null && outProgress != null) {
    return LayerAnimationProgress(math.min(inProgress, outProgress));
  }
  final progress = inProgress ?? outProgress;
  return progress == null ? null : LayerAnimationProgress(progress);
}