Add reverse functionality to repeat() of AnimationController (#25125)
This commit is contained in:
parent
c362d8da07
commit
d1bde2edde
3
AUTHORS
3
AUTHORS
@ -34,4 +34,5 @@ Jasper van Riet <jaspervanriet@gmail.com>
|
||||
Mattijs Fuijkschot <mattijs.fuijkschot@gmail.com>
|
||||
TruongSinh Tran-Nguyen <i@truongsinh.pro>
|
||||
Sander Dalby Larsen <srdlarsen@gmail.com>
|
||||
Marco Scannadinari <m@scannadinari.co.uk>
|
||||
Marco Scannadinari <m@scannadinari.co.uk>
|
||||
Frederik Schweiger <mail@flschweiger.net>
|
||||
|
@ -579,7 +579,11 @@ class AnimationController extends Animation<double>
|
||||
/// Starts running this animation in the forward direction, and
|
||||
/// restarts the animation when it completes.
|
||||
///
|
||||
/// Defaults to repeating between the lower and upper bounds.
|
||||
/// Defaults to repeating between the [lowerBound] and [upperBound] of the
|
||||
/// [AnimationController] when no explicit value is set for [min] and [max].
|
||||
///
|
||||
/// With [reverse] set to true, instead of always starting over at [min]
|
||||
/// the value will alternate between [min] and [max] values on each repeat.
|
||||
///
|
||||
/// Returns a [TickerFuture] that never completes. The [TickerFuture.orCancel] future
|
||||
/// completes with an error when the animation is stopped (e.g. with [stop]).
|
||||
@ -587,7 +591,7 @@ class AnimationController extends Animation<double>
|
||||
/// The most recently returned [TickerFuture], if any, is marked as having been
|
||||
/// canceled, meaning the future never completes and its [TickerFuture.orCancel]
|
||||
/// derivative future completes with a [TickerCanceled] error.
|
||||
TickerFuture repeat({ double min, double max, Duration period }) {
|
||||
TickerFuture repeat({ double min, double max, bool reverse = false, Duration period }) {
|
||||
min ??= lowerBound;
|
||||
max ??= upperBound;
|
||||
period ??= duration;
|
||||
@ -602,7 +606,10 @@ class AnimationController extends Animation<double>
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
return animateWith(_RepeatingSimulation(min, max, period));
|
||||
assert(max >= min);
|
||||
assert(max <= upperBound && min >= lowerBound);
|
||||
assert(reverse != null);
|
||||
return animateWith(_RepeatingSimulation(_value, min, max, reverse, period));
|
||||
}
|
||||
|
||||
/// Drives the animation with a critically damped spring (within [lowerBound]
|
||||
@ -789,21 +796,33 @@ class _InterpolationSimulation extends Simulation {
|
||||
}
|
||||
|
||||
class _RepeatingSimulation extends Simulation {
|
||||
_RepeatingSimulation(this.min, this.max, Duration period)
|
||||
: _periodInSeconds = period.inMicroseconds / Duration.microsecondsPerSecond {
|
||||
_RepeatingSimulation(double initialValue, this.min, this.max, this.reverse, Duration period)
|
||||
: _periodInSeconds = period.inMicroseconds / Duration.microsecondsPerSecond,
|
||||
_initialT = (max == min) ? 0.0 : (initialValue / (max - min)) * (period.inMicroseconds / Duration.microsecondsPerSecond) {
|
||||
assert(_periodInSeconds > 0.0);
|
||||
assert(_initialT >= 0.0);
|
||||
}
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final bool reverse;
|
||||
|
||||
final double _periodInSeconds;
|
||||
final double _initialT;
|
||||
|
||||
@override
|
||||
double x(double timeInSeconds) {
|
||||
assert(timeInSeconds >= 0.0);
|
||||
final double t = (timeInSeconds / _periodInSeconds) % 1.0;
|
||||
return ui.lerpDouble(min, max, t);
|
||||
|
||||
final double totalTimeInSeconds = timeInSeconds + _initialT;
|
||||
final double t = (totalTimeInSeconds / _periodInSeconds) % 1.0;
|
||||
final bool _isPlayingReverse = (totalTimeInSeconds ~/ _periodInSeconds) % 2 == 1;
|
||||
|
||||
if (reverse && _isPlayingReverse) {
|
||||
return ui.lerpDouble(max, min, t);
|
||||
} else {
|
||||
return ui.lerpDouble(min, max, t);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -581,6 +581,93 @@ void main() {
|
||||
statusLog.clear();
|
||||
});
|
||||
|
||||
test('calling repeat with reverse set to true makes the animation alternate '
|
||||
'between lowerBound and upperBound values on each repeat', () {
|
||||
final AnimationController controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
value: 0.0,
|
||||
lowerBound: 0.0,
|
||||
upperBound: 1.0,
|
||||
vsync: const TestVSync(),
|
||||
);
|
||||
|
||||
expect(controller.value, 0.0);
|
||||
|
||||
controller.repeat(reverse: true);
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 25));
|
||||
expect(controller.value, 0.25);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 125));
|
||||
expect(controller.value, 0.75);
|
||||
|
||||
controller.reset();
|
||||
controller.value = 1.0;
|
||||
expect(controller.value, 1.0);
|
||||
|
||||
controller.repeat(reverse: true);
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 25));
|
||||
expect(controller.value, 0.75);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 125));
|
||||
expect(controller.value, 0.25);
|
||||
|
||||
controller.reset();
|
||||
controller.value = 0.5;
|
||||
expect(controller.value, 0.5);
|
||||
|
||||
controller.repeat(reverse: true);
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 50));
|
||||
expect(controller.value, 1.0);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 150));
|
||||
expect(controller.value, 0.0);
|
||||
});
|
||||
|
||||
test('calling repeat with specified min and max values makes the animation '
|
||||
'alternate between min and max values on each repeat', () {
|
||||
final AnimationController controller = AnimationController(
|
||||
duration: const Duration(milliseconds: 100),
|
||||
value: 0.0,
|
||||
lowerBound: 0.0,
|
||||
upperBound: 1.0,
|
||||
vsync: const TestVSync(),
|
||||
);
|
||||
|
||||
expect(controller.value, 0.0);
|
||||
|
||||
controller.repeat(reverse: true, min: 0.5, max: 1.0);
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 50));
|
||||
expect(controller.value, 0.75);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 100));
|
||||
expect(controller.value, 1.00);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 200));
|
||||
expect(controller.value, 0.5);
|
||||
|
||||
controller.reset();
|
||||
controller.value = 0.0;
|
||||
expect(controller.value, 0.0);
|
||||
|
||||
controller.repeat(reverse: true, min: 1.0, max: 1.0);
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 25));
|
||||
expect(controller.value, 1.0);
|
||||
|
||||
tick(const Duration(milliseconds: 0));
|
||||
tick(const Duration(milliseconds: 125));
|
||||
expect(controller.value, 1.0);
|
||||
});
|
||||
|
||||
group('AnimationBehavior', () {
|
||||
test('Default values for constructor', () {
|
||||
final AnimationController controller = AnimationController(vsync: const TestVSync());
|
||||
|
Loading…
x
Reference in New Issue
Block a user