Simplify Scrollable animations
Rather than having two objects driving scrolling animations, we now have one object, a Timeline, drive both scrollTo and fling animations. Using Timeline instead of AnimatedSimulation paves the way to removing AnimatedSimulation (which is now used only inside the animation library). Finally, this patch also simplifies (and makes private) _TweenSimulation by using AnimatedValue to do the math.
This commit is contained in:
parent
203e6fd7e8
commit
49aba0cc0c
@ -85,7 +85,7 @@ class AnimationPerformance implements WatchableAnimationPerformance {
|
|||||||
/// The progress of this performance along the timeline
|
/// The progress of this performance along the timeline
|
||||||
///
|
///
|
||||||
/// Note: Setting this value stops the current animation.
|
/// Note: Setting this value stops the current animation.
|
||||||
double get progress => _timeline.value;
|
double get progress => _timeline.value.clamp(0.0, 1.0);
|
||||||
void set progress(double t) {
|
void set progress(double t) {
|
||||||
// TODO(mpcomplete): should this affect |direction|?
|
// TODO(mpcomplete): should this affect |direction|?
|
||||||
stop();
|
stop();
|
||||||
|
@ -5,30 +5,28 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:newton/newton.dart';
|
import 'package:newton/newton.dart';
|
||||||
|
import 'package:sky/src/animation/curves.dart';
|
||||||
|
import 'package:sky/src/animation/animated_value.dart';
|
||||||
import 'package:sky/src/animation/animated_simulation.dart';
|
import 'package:sky/src/animation/animated_simulation.dart';
|
||||||
|
|
||||||
/// A simulation that linearly varies from [begin] to [end] over [duration]
|
/// A simulation that linearly varies from [begin] to [end] over [duration]
|
||||||
class TweenSimulation extends Simulation {
|
class _TweenSimulation extends Simulation {
|
||||||
final double _durationInSeconds;
|
final double _durationInSeconds;
|
||||||
|
final AnimatedValue<double> _tween;
|
||||||
|
|
||||||
/// The initial value of the simulation
|
_TweenSimulation(double begin, double end, Duration duration, Curve curve)
|
||||||
final double begin;
|
: _durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND,
|
||||||
|
_tween = new AnimatedValue<double>(begin, end: end, curve: curve) {
|
||||||
/// The terminal value of the simulation
|
|
||||||
final double end;
|
|
||||||
|
|
||||||
TweenSimulation(Duration duration, this.begin, this.end) :
|
|
||||||
_durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
|
|
||||||
assert(_durationInSeconds > 0.0);
|
assert(_durationInSeconds > 0.0);
|
||||||
assert(begin != null && begin >= 0.0 && begin <= 1.0);
|
assert(begin != null);
|
||||||
assert(end != null && end >= 0.0 && end <= 1.0);
|
assert(end != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
double x(double timeInSeconds) {
|
double x(double timeInSeconds) {
|
||||||
assert(timeInSeconds >= 0.0);
|
assert(timeInSeconds >= 0.0);
|
||||||
final double t = timeInSeconds / _durationInSeconds;
|
final double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
|
||||||
return t >= 1.0 ? end : begin + (end - begin) * t;
|
_tween.setProgress(t, Direction.forward);
|
||||||
|
return _tween.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
double dx(double timeInSeconds) => 1.0;
|
double dx(double timeInSeconds) => 1.0;
|
||||||
@ -45,9 +43,9 @@ class Timeline {
|
|||||||
AnimatedSimulation _animation;
|
AnimatedSimulation _animation;
|
||||||
|
|
||||||
/// The current value of the timeline
|
/// The current value of the timeline
|
||||||
double get value => _animation.value.clamp(0.0, 1.0);
|
double get value => _animation.value;
|
||||||
void set value(double newValue) {
|
void set value(double newValue) {
|
||||||
assert(newValue != null && newValue >= 0.0 && newValue <= 1.0);
|
assert(newValue != null);
|
||||||
assert(!isAnimating);
|
assert(!isAnimating);
|
||||||
_animation.value = newValue;
|
_animation.value = newValue;
|
||||||
}
|
}
|
||||||
@ -55,23 +53,14 @@ class Timeline {
|
|||||||
/// Whether the timeline is currently animating
|
/// Whether the timeline is currently animating
|
||||||
bool get isAnimating => _animation.isAnimating;
|
bool get isAnimating => _animation.isAnimating;
|
||||||
|
|
||||||
Future _start({
|
|
||||||
Duration duration,
|
|
||||||
double begin: 0.0,
|
|
||||||
double end: 1.0
|
|
||||||
}) {
|
|
||||||
assert(!_animation.isAnimating);
|
|
||||||
assert(duration > Duration.ZERO);
|
|
||||||
return _animation.start(new TweenSimulation(duration, begin, end));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Animate value of the timeline to the given target over the given duration
|
/// Animate value of the timeline to the given target over the given duration
|
||||||
///
|
///
|
||||||
/// Returns a future that resolves when the timeline stops animating,
|
/// Returns a future that resolves when the timeline stops animating,
|
||||||
/// typically when the timeline arives at the target value.
|
/// typically when the timeline arives at the target value.
|
||||||
Future animateTo(double target, { Duration duration }) {
|
Future animateTo(double target, { Duration duration, Curve curve: linear }) {
|
||||||
assert(duration > Duration.ZERO);
|
assert(duration > Duration.ZERO);
|
||||||
return _start(duration: duration, begin: value, end: target);
|
assert(!_animation.isAnimating);
|
||||||
|
return _animation.start(new _TweenSimulation(value, target, duration, curve));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop animating the timeline
|
/// Stop animating the timeline
|
||||||
@ -79,7 +68,7 @@ class Timeline {
|
|||||||
_animation.stop();
|
_animation.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gives the given simulation control over the timeline
|
/// Gives the given simulation control over the timeline
|
||||||
Future fling(Simulation simulation) {
|
Future fling(Simulation simulation) {
|
||||||
stop();
|
stop();
|
||||||
return _animation.start(simulation);
|
return _animation.start(simulation);
|
||||||
|
@ -51,16 +51,10 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
if (config.initialScrollOffset is double)
|
if (config.initialScrollOffset is double)
|
||||||
_scrollOffset = config.initialScrollOffset;
|
_scrollOffset = config.initialScrollOffset;
|
||||||
_toEndAnimation = new AnimatedSimulation(_setScrollOffset);
|
_animation = new Timeline(_setScrollOffset);
|
||||||
_toOffsetAnimation = new ValueAnimation<double>()
|
|
||||||
..addListener(() {
|
|
||||||
AnimatedValue<double> offset = _toOffsetAnimation.variable;
|
|
||||||
_setScrollOffset(offset.value);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatedSimulation _toEndAnimation; // See _startToEndAnimation()
|
Timeline _animation;
|
||||||
ValueAnimation<double> _toOffsetAnimation; // Started by scrollTo()
|
|
||||||
|
|
||||||
double _scrollOffset = 0.0;
|
double _scrollOffset = 0.0;
|
||||||
double get scrollOffset => _scrollOffset;
|
double get scrollOffset => _scrollOffset;
|
||||||
@ -106,23 +100,10 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
|
|
||||||
Widget buildContent(BuildContext context);
|
Widget buildContent(BuildContext context);
|
||||||
|
|
||||||
Future _startToOffsetAnimation(double newScrollOffset, Duration duration, Curve curve) {
|
Future _animateTo(double newScrollOffset, Duration duration, Curve curve) {
|
||||||
_stopAnimations();
|
_animation.stop();
|
||||||
_toOffsetAnimation
|
_animation.value = scrollOffset;
|
||||||
..variable = new AnimatedValue<double>(scrollOffset,
|
return _animation.animateTo(newScrollOffset, duration: duration, curve: curve);
|
||||||
end: newScrollOffset,
|
|
||||||
curve: curve
|
|
||||||
)
|
|
||||||
..progress = 0.0
|
|
||||||
..duration = duration;
|
|
||||||
return _toOffsetAnimation.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stopAnimations() {
|
|
||||||
if (_toOffsetAnimation.isAnimating)
|
|
||||||
_toOffsetAnimation.stop();
|
|
||||||
if (_toEndAnimation.isAnimating)
|
|
||||||
_toEndAnimation.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _scrollOffsetIsInBounds(double offset) {
|
bool _scrollOffsetIsInBounds(double offset) {
|
||||||
@ -165,16 +146,16 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future _startToEndAnimation({ double velocity }) {
|
Future _startToEndAnimation({ double velocity }) {
|
||||||
_stopAnimations();
|
_animation.stop();
|
||||||
Simulation simulation =
|
Simulation simulation =
|
||||||
_createSnapSimulation(velocity) ?? _createFlingSimulation(velocity ?? 0.0);
|
_createSnapSimulation(velocity) ?? _createFlingSimulation(velocity ?? 0.0);
|
||||||
if (simulation == null)
|
if (simulation == null)
|
||||||
return new Future.value();
|
return new Future.value();
|
||||||
return _toEndAnimation.start(simulation);
|
return _animation.fling(simulation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_stopAnimations();
|
_animation.stop();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,12 +174,12 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
return new Future.value();
|
return new Future.value();
|
||||||
|
|
||||||
if (duration == null) {
|
if (duration == null) {
|
||||||
_stopAnimations();
|
_animation.stop();
|
||||||
_setScrollOffset(newScrollOffset);
|
_setScrollOffset(newScrollOffset);
|
||||||
return new Future.value();
|
return new Future.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _startToOffsetAnimation(newScrollOffset, duration, curve);
|
return _animateTo(newScrollOffset, duration, curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) {
|
Future scrollBy(double scrollDelta, { Duration duration, Curve curve }) {
|
||||||
@ -209,7 +190,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
Future fling(Offset velocity) {
|
Future fling(Offset velocity) {
|
||||||
if (velocity != Offset.zero)
|
if (velocity != Offset.zero)
|
||||||
return _startToEndAnimation(velocity: _scrollVelocity(velocity));
|
return _startToEndAnimation(velocity: _scrollVelocity(velocity));
|
||||||
if (!_toEndAnimation.isAnimating && (_toOffsetAnimation == null || !_toOffsetAnimation.isAnimating))
|
if (!_animation.isAnimating)
|
||||||
return settleScrollOffset();
|
return settleScrollOffset();
|
||||||
return new Future.value();
|
return new Future.value();
|
||||||
}
|
}
|
||||||
@ -226,7 +207,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handlePointerDown(_) {
|
void _handlePointerDown(_) {
|
||||||
_stopAnimations();
|
_animation.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragUpdate(double delta) {
|
void _handleDragUpdate(double delta) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user