layerAnimationProgress function
- LayerAnimation animation,
- Duration time, {
- required Duration? start,
- 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). A spin and a flip turn on instead,
by LayerAnimation.turns eased over each cycle (see
LayerAnimationProgress.turn).
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 (_turns(animation.type)) {
final turns = animation.turns ?? LayerAnimation.defaultTurns;
// The cycles already done, without their whole turns, so a long video
// does not lose precision.
final done = (elapsed ~/ durationUs * turns) % 1;
return LayerAnimationProgress(
1,
turn:
done +
turns * applyLayerAnimationCurve(curve, inCycle / 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.
final double progress;
final bool entering;
if (inProgress != null && (outProgress == null || inProgress < outProgress)) {
progress = inProgress;
entering = true;
} else if (outProgress != null) {
progress = outProgress;
entering = false;
} else {
return null;
}
if (!_turns(animation.type)) return LayerAnimationProgress(progress);
final away =
(1 - progress) * (animation.turns ?? LayerAnimation.defaultTurns);
return LayerAnimationProgress(progress, turn: entering ? -away : away);
}