mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Merge pull request #1085 from abarth/docs_animation2
Add dartdoc for the animation library
This commit is contained in:
commit
0349d441d2
@ -9,14 +9,22 @@ import 'package:sky/src/animation/scheduler.dart';
|
||||
|
||||
const double _kSecondsPerMillisecond = 1000.0;
|
||||
|
||||
class Ticker {
|
||||
Ticker(Function onTick) : _onTick = onTick;
|
||||
// TODO(abarth): Change from double to Duration.
|
||||
typedef _TickerCallback(double timeStamp);
|
||||
|
||||
final Function _onTick;
|
||||
/// Calls its callback once per animation frame
|
||||
class Ticker {
|
||||
/// Constructs a ticker that will call onTick once per frame while running
|
||||
Ticker(_TickerCallback onTick) : _onTick = onTick;
|
||||
|
||||
final _TickerCallback _onTick;
|
||||
|
||||
Completer _completer;
|
||||
int _animationId;
|
||||
|
||||
/// Start calling onTick once per animation frame
|
||||
///
|
||||
/// The returned future resolves once the ticker stops ticking.
|
||||
Future start() {
|
||||
assert(!isTicking);
|
||||
_completer = new Completer();
|
||||
@ -24,6 +32,9 @@ class Ticker {
|
||||
return _completer.future;
|
||||
}
|
||||
|
||||
/// Stop calling onTick
|
||||
///
|
||||
/// Causes the future returned by [start] to resolve.
|
||||
void stop() {
|
||||
if (!isTicking)
|
||||
return;
|
||||
@ -42,6 +53,7 @@ class Ticker {
|
||||
localCompleter.complete();
|
||||
}
|
||||
|
||||
/// Whether this ticker has scheduled a call to onTick
|
||||
bool get isTicking => _completer != null;
|
||||
|
||||
void _tick(double timeStamp) {
|
||||
@ -63,6 +75,7 @@ class Ticker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ticks a simulation once per frame
|
||||
class AnimatedSimulation {
|
||||
|
||||
AnimatedSimulation(Function onTick) : _onTick = onTick {
|
||||
@ -76,6 +89,7 @@ class AnimatedSimulation {
|
||||
double _startTime;
|
||||
|
||||
double _value = 0.0;
|
||||
/// The current value of the simulation
|
||||
double get value => _value;
|
||||
void set value(double newValue) {
|
||||
assert(!_ticker.isTicking);
|
||||
@ -83,6 +97,9 @@ class AnimatedSimulation {
|
||||
_onTick(_value);
|
||||
}
|
||||
|
||||
/// Start ticking the given simulation once per frame
|
||||
///
|
||||
/// Returns a future that resolves when the simulation stops ticking.
|
||||
Future start(Simulation simulation) {
|
||||
assert(simulation != null);
|
||||
assert(!_ticker.isTicking);
|
||||
@ -92,12 +109,14 @@ class AnimatedSimulation {
|
||||
return _ticker.start();
|
||||
}
|
||||
|
||||
/// Stop ticking the current simulation
|
||||
void stop() {
|
||||
_simulation = null;
|
||||
_startTime = null;
|
||||
_ticker.stop();
|
||||
}
|
||||
|
||||
/// Whether this object is currently ticking a simulation
|
||||
bool get isAnimating => _ticker.isTicking;
|
||||
|
||||
void _tick(double timeStamp) {
|
||||
|
||||
@ -6,24 +6,48 @@ import "dart:sky";
|
||||
|
||||
import 'package:sky/src/animation/curves.dart';
|
||||
|
||||
/// The direction in which an animation is running
|
||||
enum Direction {
|
||||
/// The animation is running from beginning to end
|
||||
forward,
|
||||
|
||||
/// The animation is running backwards, from end to beginning
|
||||
reverse
|
||||
}
|
||||
|
||||
/// A variable that changes as an animation progresses
|
||||
abstract class AnimatedVariable {
|
||||
/// Update the variable to a given time in an animation that is running in the given direction
|
||||
void setProgress(double t, Direction direction);
|
||||
String toString();
|
||||
}
|
||||
|
||||
///
|
||||
class AnimationTiming {
|
||||
AnimationTiming({this.interval, this.reverseInterval, this.curve, this.reverseCurve});
|
||||
AnimationTiming({
|
||||
this.interval,
|
||||
this.reverseInterval,
|
||||
this.curve,
|
||||
this.reverseCurve
|
||||
});
|
||||
|
||||
/// The interval during which this timing is active in the forward direction
|
||||
Interval interval;
|
||||
|
||||
/// The interval during which this timing is active in the reverse direction
|
||||
///
|
||||
/// If this field is null, the timing defaules to using [interval] in both directions.
|
||||
Interval reverseInterval;
|
||||
|
||||
/// The curve that this timing applies to the animation clock in the forward direction
|
||||
Curve curve;
|
||||
|
||||
/// The curve that this timing applies to the animation clock in the reverse direction
|
||||
///
|
||||
/// If this field is null, the timing defaules to using [curve] in both directions.
|
||||
Curve reverseCurve;
|
||||
|
||||
/// Applies this timing to the given animation clock value in the given direction
|
||||
double transform(double t, Direction direction) {
|
||||
Interval interval = _getInterval(direction);
|
||||
if (interval != null)
|
||||
@ -49,18 +73,26 @@ class AnimationTiming {
|
||||
}
|
||||
}
|
||||
|
||||
/// An animated variable with a concrete type
|
||||
class AnimatedValue<T extends dynamic> extends AnimationTiming implements AnimatedVariable {
|
||||
AnimatedValue(this.begin, { this.end, Interval interval, Curve curve, Curve reverseCurve })
|
||||
: super(interval: interval, curve: curve, reverseCurve: reverseCurve) {
|
||||
value = begin;
|
||||
}
|
||||
|
||||
/// The current value of this variable
|
||||
T value;
|
||||
|
||||
/// The value this variable has at the beginning of the animation
|
||||
T begin;
|
||||
|
||||
/// The value this variable has at the end of the animation
|
||||
T end;
|
||||
|
||||
/// Returns the value this variable has at the given animation clock value
|
||||
T lerp(double t) => begin + (end - begin) * t;
|
||||
|
||||
/// Updates the value of this variable according to the given animation clock value and direction
|
||||
void setProgress(double t, Direction direction) {
|
||||
if (end != null) {
|
||||
t = transform(t, direction);
|
||||
@ -71,12 +103,15 @@ class AnimatedValue<T extends dynamic> extends AnimationTiming implements Animat
|
||||
String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)';
|
||||
}
|
||||
|
||||
/// A list of animated variables
|
||||
class AnimatedList extends AnimationTiming implements AnimatedVariable {
|
||||
/// The list of variables contained in the list
|
||||
List<AnimatedVariable> variables;
|
||||
|
||||
AnimatedList(this.variables, { Interval interval, Curve curve, Curve reverseCurve })
|
||||
: super(interval: interval, curve: curve, reverseCurve: reverseCurve);
|
||||
|
||||
// Updates the value of all the variables in the list according to the given animation clock value and direction
|
||||
void setProgress(double t, Direction direction) {
|
||||
double adjustedTime = transform(t, direction);
|
||||
for (AnimatedVariable variable in variables)
|
||||
@ -86,6 +121,10 @@ class AnimatedList extends AnimationTiming implements AnimatedVariable {
|
||||
String toString() => 'AnimatedList([$variables])';
|
||||
}
|
||||
|
||||
/// An animated variable containing a color
|
||||
///
|
||||
/// This class specializes the interpolation of AnimatedValue<Color> to be
|
||||
/// appropriate for colors.
|
||||
class AnimatedColorValue extends AnimatedValue<Color> {
|
||||
AnimatedColorValue(Color begin, { Color end, Curve curve })
|
||||
: super(begin, end: end, curve: curve);
|
||||
@ -93,6 +132,10 @@ class AnimatedColorValue extends AnimatedValue<Color> {
|
||||
Color lerp(double t) => Color.lerp(begin, end, t);
|
||||
}
|
||||
|
||||
/// An animated variable containing a rectangle
|
||||
///
|
||||
/// This class specializes the interpolation of AnimatedValue<Rect> to be
|
||||
/// appropriate for rectangles.
|
||||
class AnimatedRect extends AnimatedValue<Rect> {
|
||||
AnimatedRect(Rect begin, { Rect end, Curve curve })
|
||||
: super(begin, end: end, curve: curve);
|
||||
|
||||
@ -8,50 +8,62 @@ import 'package:sky/src/animation/animated_value.dart';
|
||||
import 'package:sky/src/animation/forces.dart';
|
||||
import 'package:sky/src/animation/timeline.dart';
|
||||
|
||||
/// The status of an animation
|
||||
enum AnimationStatus {
|
||||
dismissed, // stoped at 0
|
||||
forward, // animating from 0 => 1
|
||||
reverse, // animating from 1 => 0
|
||||
completed, // stopped at 1
|
||||
/// The animation is stopped at the beginning
|
||||
dismissed,
|
||||
|
||||
/// The animation is running from beginning to end
|
||||
forward,
|
||||
|
||||
/// The animation is running backwards, from end to beginning
|
||||
reverse,
|
||||
|
||||
/// The animation is stopped at the end
|
||||
completed,
|
||||
}
|
||||
|
||||
// This class manages a "performance" - a collection of values that change
|
||||
// based on a timeline. For example, a performance may handle an animation
|
||||
// of a menu opening by sliding and fading in (changing Y value and opacity)
|
||||
// over .5 seconds. The performance can move forwards (present) or backwards
|
||||
// (dismiss). A consumer may also take direct control of the timeline by
|
||||
// manipulating |progress|, or |fling| the timeline causing a physics-based
|
||||
// simulation to take over the progression.
|
||||
/// A collection of values that animated based on a timeline
|
||||
///
|
||||
/// For example, a performance may handle an animation of a menu opening by
|
||||
/// sliding and fading in (changing Y value and opacity) over .5 seconds. The
|
||||
/// performance can move forwards (present) or backwards (dismiss). A consumer
|
||||
/// may also take direct control of the timeline by manipulating [progress], or
|
||||
/// [fling] the timeline causing a physics-based simulation to take over the
|
||||
/// progression.
|
||||
class AnimationPerformance {
|
||||
AnimationPerformance({AnimatedVariable variable, this.duration}) :
|
||||
_variable = variable {
|
||||
_timeline = new Timeline(_tick);
|
||||
}
|
||||
|
||||
AnimatedVariable _variable;
|
||||
/// The length of time this performance should last
|
||||
Duration duration;
|
||||
|
||||
/// The variable being updated by this performance
|
||||
AnimatedVariable get variable => _variable;
|
||||
void set variable(AnimatedVariable v) { _variable = v; }
|
||||
void set variable(AnimatedVariable variable) { _variable = variable; }
|
||||
AnimatedVariable _variable;
|
||||
|
||||
// Advances from 0 to 1. On each tick, we'll update our variable's values.
|
||||
Timeline _timeline;
|
||||
Timeline get timeline => _timeline;
|
||||
|
||||
Direction _direction;
|
||||
Direction get direction => _direction;
|
||||
|
||||
// This controls which curve we use for variables with different curves in
|
||||
// the forward/reverse directions. Curve direction is only reset when we hit
|
||||
// 0 or 1, to avoid discontinuities.
|
||||
/// The direction used to select the current curve
|
||||
///
|
||||
/// Curve direction is only reset when we hit the beginning or the end of the
|
||||
/// timeline to avoid discontinuities in the value of the variable.
|
||||
Direction _curveDirection;
|
||||
Direction get curveDirection => _curveDirection;
|
||||
|
||||
/// If non-null, animate with this timing instead of a linear timing
|
||||
AnimationTiming timing;
|
||||
|
||||
// If non-null, animate with this force instead of a tween animation.
|
||||
/// If non-null, animate with this force instead of a zero-to-one timeline.
|
||||
Force attachedForce;
|
||||
|
||||
/// Add a variable to this animation
|
||||
///
|
||||
/// If there are no attached variables, this variable becomes the value of
|
||||
/// [variable]. Otherwise, all the variables are stored in an [AnimatedList].
|
||||
void addVariable(AnimatedVariable newVariable) {
|
||||
if (variable == null) {
|
||||
variable = newVariable;
|
||||
@ -62,70 +74,92 @@ class AnimationPerformance {
|
||||
}
|
||||
}
|
||||
|
||||
double get progress => timeline.value;
|
||||
/// The progress of this performance along the timeline
|
||||
///
|
||||
/// Note: Setting this value stops the current animation.
|
||||
double get progress => _timeline.value;
|
||||
void set progress(double t) {
|
||||
// TODO(mpcomplete): should this affect |direction|?
|
||||
stop();
|
||||
timeline.value = t.clamp(0.0, 1.0);
|
||||
_timeline.value = t.clamp(0.0, 1.0);
|
||||
_checkStatusChanged();
|
||||
}
|
||||
|
||||
double get curvedProgress {
|
||||
return timing != null ? timing.transform(progress, curveDirection) : progress;
|
||||
double get _curvedProgress {
|
||||
return timing != null ? timing.transform(progress, _curveDirection) : progress;
|
||||
}
|
||||
|
||||
/// Whether this animation is stopped at the beginning
|
||||
bool get isDismissed => status == AnimationStatus.dismissed;
|
||||
bool get isCompleted => status == AnimationStatus.completed;
|
||||
bool get isAnimating => timeline.isAnimating;
|
||||
|
||||
/// Whether this animation is stopped at the end
|
||||
bool get isCompleted => status == AnimationStatus.completed;
|
||||
|
||||
/// Whether this animation is currently animating in either the forward or reverse direction
|
||||
bool get isAnimating => _timeline.isAnimating;
|
||||
|
||||
/// The current status of this animation
|
||||
AnimationStatus get status {
|
||||
if (!isAnimating && progress == 1.0)
|
||||
return AnimationStatus.completed;
|
||||
if (!isAnimating && progress == 0.0)
|
||||
return AnimationStatus.dismissed;
|
||||
return direction == Direction.forward ?
|
||||
return _direction == Direction.forward ?
|
||||
AnimationStatus.forward :
|
||||
AnimationStatus.reverse;
|
||||
}
|
||||
|
||||
/// Update the given varaible according to the current progress of this performance
|
||||
void updateVariable(AnimatedVariable variable) {
|
||||
variable.setProgress(curvedProgress, curveDirection);
|
||||
variable.setProgress(_curvedProgress, _curveDirection);
|
||||
}
|
||||
|
||||
/// Start running this animation in the given direction
|
||||
Future play([Direction direction = Direction.forward]) {
|
||||
_direction = direction;
|
||||
return resume();
|
||||
}
|
||||
|
||||
/// Start running this animation forwards (towards the end)
|
||||
Future forward() => play(Direction.forward);
|
||||
|
||||
/// Start running this animation in reverse (towards the beginning)
|
||||
Future reverse() => play(Direction.reverse);
|
||||
|
||||
/// Start running this animation in the most recently direction
|
||||
Future resume() {
|
||||
if (attachedForce != null) {
|
||||
return fling(velocity: _direction == Direction.forward ? 1.0 : -1.0,
|
||||
force: attachedForce);
|
||||
}
|
||||
return _animateTo(direction == Direction.forward ? 1.0 : 0.0);
|
||||
return _animateTo(_direction == Direction.forward ? 1.0 : 0.0);
|
||||
}
|
||||
|
||||
/// Stop running this animation
|
||||
void stop() {
|
||||
timeline.stop();
|
||||
_timeline.stop();
|
||||
}
|
||||
|
||||
// Flings the timeline with an optional force (defaults to a critically
|
||||
// damped spring) and initial velocity. If velocity is positive, the
|
||||
// animation will complete, otherwise it will dismiss.
|
||||
/// Start running this animation according to the given physical parameters
|
||||
///
|
||||
/// Flings the timeline with an optional force (defaults to a critically
|
||||
/// damped spring) and initial velocity. If velocity is positive, the
|
||||
/// animation will complete, otherwise it will dismiss.
|
||||
Future fling({double velocity: 1.0, Force force}) {
|
||||
if (force == null)
|
||||
force = kDefaultSpringForce;
|
||||
_direction = velocity < 0.0 ? Direction.reverse : Direction.forward;
|
||||
return timeline.fling(force.release(progress, velocity));
|
||||
return _timeline.fling(force.release(progress, velocity));
|
||||
}
|
||||
|
||||
final List<Function> _listeners = new List<Function>();
|
||||
|
||||
/// Calls the listener every time the progress of this performance changes
|
||||
void addListener(Function listener) {
|
||||
_listeners.add(listener);
|
||||
}
|
||||
|
||||
/// Stop calling the listener every time the progress of this performance changes
|
||||
void removeListener(Function listener) {
|
||||
_listeners.remove(listener);
|
||||
}
|
||||
@ -138,10 +172,12 @@ class AnimationPerformance {
|
||||
|
||||
final List<Function> _statusListeners = new List<Function>();
|
||||
|
||||
/// Calls listener every time the status of this performance changes
|
||||
void addStatusListener(Function listener) {
|
||||
_statusListeners.add(listener);
|
||||
}
|
||||
|
||||
/// Stops calling the listener every time the status of this performance changes
|
||||
void removeStatusListener(Function listener) {
|
||||
_statusListeners.remove(listener);
|
||||
}
|
||||
@ -160,28 +196,28 @@ class AnimationPerformance {
|
||||
void _updateCurveDirection() {
|
||||
if (status != _lastStatus) {
|
||||
if (_lastStatus == AnimationStatus.dismissed || _lastStatus == AnimationStatus.completed)
|
||||
_curveDirection = direction;
|
||||
_curveDirection = _direction;
|
||||
}
|
||||
}
|
||||
|
||||
Future _animateTo(double target) {
|
||||
Duration remainingDuration = duration * (target - timeline.value).abs();
|
||||
timeline.stop();
|
||||
Duration remainingDuration = duration * (target - _timeline.value).abs();
|
||||
_timeline.stop();
|
||||
if (remainingDuration == Duration.ZERO)
|
||||
return new Future.value();
|
||||
return timeline.animateTo(target, duration: remainingDuration);
|
||||
return _timeline.animateTo(target, duration: remainingDuration);
|
||||
}
|
||||
|
||||
void _tick(double t) {
|
||||
_updateCurveDirection();
|
||||
if (variable != null)
|
||||
variable.setProgress(curvedProgress, curveDirection);
|
||||
variable.setProgress(_curvedProgress, _curveDirection);
|
||||
_notifyListeners();
|
||||
_checkStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// Simple helper class for an animation with a single value.
|
||||
/// An animation performance with an animated variable with a concrete type
|
||||
class ValueAnimation<T> extends AnimationPerformance {
|
||||
ValueAnimation({AnimatedValue<T> variable, Duration duration}) :
|
||||
super(variable: variable, duration: duration);
|
||||
|
||||
@ -11,10 +11,17 @@ double _evaluateCubic(double a, double b, double m) {
|
||||
|
||||
const double _kCubicErrorBound = 0.001;
|
||||
|
||||
/// A mapping of the unit interval to the unit interval
|
||||
///
|
||||
/// A curve must map 0.0 to 0.0 and 1.0 to 1.0.
|
||||
abstract class Curve {
|
||||
/// Return the value of the curve at point t
|
||||
///
|
||||
/// The value of t must be between 0.0 and 1.0, inclusive.
|
||||
double transform(double t);
|
||||
}
|
||||
|
||||
/// The idenity map over the unit interval
|
||||
class Linear implements Curve {
|
||||
const Linear();
|
||||
|
||||
@ -23,8 +30,12 @@ class Linear implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// A curve that is initially 0.0, then linear, then 1.0
|
||||
class Interval implements Curve {
|
||||
/// The smallest value for which this interval is 0.0
|
||||
final double start;
|
||||
|
||||
/// The smallest value for which this interval is 1.0
|
||||
final double end;
|
||||
|
||||
Interval(this.start, this.end) {
|
||||
@ -39,6 +50,7 @@ class Interval implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// A cubic polynomial mapping of the unit interval
|
||||
class Cubic implements Curve {
|
||||
final double a;
|
||||
final double b;
|
||||
@ -79,6 +91,7 @@ double _bounce(double t) {
|
||||
return 7.5625 * t * t + 0.984375;
|
||||
}
|
||||
|
||||
/// An oscillating curve that grows in magnitude
|
||||
class BounceInCurve implements Curve {
|
||||
const BounceInCurve();
|
||||
|
||||
@ -87,6 +100,7 @@ class BounceInCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// An oscillating curve that shrink in magnitude
|
||||
class BounceOutCurve implements Curve {
|
||||
const BounceOutCurve();
|
||||
|
||||
@ -95,6 +109,7 @@ class BounceOutCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// An oscillating curve that first grows and then shrink in magnitude
|
||||
class BounceInOutCurve implements Curve {
|
||||
const BounceInOutCurve();
|
||||
|
||||
@ -106,6 +121,7 @@ class BounceInOutCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// An oscillating curve that grows in magnitude while overshootings its bounds
|
||||
class ElasticInCurve implements Curve {
|
||||
const ElasticInCurve([this.period = 0.4]);
|
||||
final double period;
|
||||
@ -117,6 +133,7 @@ class ElasticInCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// An oscillating curve that shrinks in magnitude while overshootings its bounds
|
||||
class ElasticOutCurve implements Curve {
|
||||
const ElasticOutCurve([this.period = 0.4]);
|
||||
final double period;
|
||||
@ -127,6 +144,7 @@ class ElasticOutCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// An oscillating curve that grows and then shrinks in magnitude while overshootings its bounds
|
||||
class ElasticInOutCurve implements Curve {
|
||||
const ElasticInOutCurve([this.period = 0.4]);
|
||||
final double period;
|
||||
@ -141,14 +159,35 @@ class ElasticInOutCurve implements Curve {
|
||||
}
|
||||
}
|
||||
|
||||
/// A linear animation curve
|
||||
const Linear linear = const Linear();
|
||||
|
||||
/// A cubic animation cuve that speeds up quickly and ends slowly
|
||||
const Cubic ease = const Cubic(0.25, 0.1, 0.25, 1.0);
|
||||
|
||||
/// A cubic animation cuve that starts slowly and ends quickly
|
||||
const Cubic easeIn = const Cubic(0.42, 0.0, 1.0, 1.0);
|
||||
|
||||
/// A cubic animation cuve that starts quickly and ends slowly
|
||||
const Cubic easeOut = const Cubic(0.0, 0.0, 0.58, 1.0);
|
||||
|
||||
/// A cubic animation cuve that starts slowly, speeds up, and then and ends slowly
|
||||
const Cubic easeInOut = const Cubic(0.42, 0.0, 0.58, 1.0);
|
||||
|
||||
/// An oscillating curve that grows in magnitude
|
||||
const BounceInCurve bounceIn = const BounceInCurve();
|
||||
|
||||
/// An oscillating curve that first grows and then shrink in magnitude
|
||||
const BounceOutCurve bounceOut = const BounceOutCurve();
|
||||
|
||||
/// An oscillating curve that first grows and then shrink in magnitude
|
||||
const BounceInOutCurve bounceInOut = const BounceInOutCurve();
|
||||
|
||||
/// An oscillating curve that grows in magnitude while overshootings its bounds
|
||||
const ElasticInCurve elasticIn = const ElasticInCurve();
|
||||
|
||||
/// An oscillating curve that shrinks in magnitude while overshootings its bounds
|
||||
const ElasticOutCurve elasticOut = const ElasticOutCurve();
|
||||
|
||||
/// An oscillating curve that grows and then shrinks in magnitude while overshootings its bounds
|
||||
const ElasticInOutCurve elasticInOut = const ElasticInOutCurve();
|
||||
|
||||
@ -4,26 +4,31 @@
|
||||
|
||||
import 'package:newton/newton.dart';
|
||||
|
||||
// Base class for creating Simulations for the animation Timeline.
|
||||
/// A factory for simulations
|
||||
abstract class Force {
|
||||
const Force();
|
||||
|
||||
Simulation release(double position, double velocity);
|
||||
}
|
||||
|
||||
/// A factory for spring-based physics simulations
|
||||
class SpringForce extends Force {
|
||||
const SpringForce(this.spring, { this.left: 0.0, this.right: 1.0 });
|
||||
|
||||
/// The description of the spring to be used in the created simulations
|
||||
final SpringDescription spring;
|
||||
|
||||
// Where to put the spring's resting point when releasing left or right,
|
||||
// respectively.
|
||||
/// Where to put the spring's resting point when releasing left
|
||||
final double left;
|
||||
|
||||
/// Where to put the spring's resting point when releasing right
|
||||
final double right;
|
||||
|
||||
// We overshoot the target by this distance, but stop the simulation when
|
||||
// the spring gets within this distance (regardless of how fast it's moving).
|
||||
// This causes the spring to settle a bit faster than it otherwise would.
|
||||
/// How pricely to terminate the simulation
|
||||
///
|
||||
/// We overshoot the target by this distance, but stop the simulation when
|
||||
/// the spring gets within this distance (regardless of how fast it's moving).
|
||||
/// This causes the spring to settle a bit faster than it otherwise would.
|
||||
static const Tolerance tolerance = const Tolerance(
|
||||
velocity: double.INFINITY,
|
||||
distance: 0.01
|
||||
@ -43,4 +48,5 @@ final SpringDescription _kDefaultSpringDesc = new SpringDescription.withDampingR
|
||||
ratio: 1.0
|
||||
);
|
||||
|
||||
/// A spring force with reasonable default values
|
||||
final SpringForce kDefaultSpringForce = new SpringForce(_kDefaultSpringDesc);
|
||||
|
||||
@ -9,24 +9,36 @@ import 'package:newton/newton.dart';
|
||||
const double _kSecondsPerMillisecond = 1000.0;
|
||||
const double _kScrollDrag = 0.025;
|
||||
|
||||
/// An interface for controlling the behavior of scrollable widgets
|
||||
abstract class ScrollBehavior {
|
||||
/// A simulation to run to determine the scroll offset
|
||||
///
|
||||
/// Called when the user stops scrolling at a given position with a given
|
||||
/// instantaneous velocity.
|
||||
Simulation release(double position, double velocity) => null;
|
||||
|
||||
// Returns the new scroll offset.
|
||||
/// The new scroll offset to use when the user attempts to scroll from the given offset by the given delta
|
||||
double applyCurve(double scrollOffset, double scrollDelta);
|
||||
}
|
||||
|
||||
/// A scroll behavior for a scrollable widget with linear extent
|
||||
abstract class ExtentScrollBehavior extends ScrollBehavior {
|
||||
ExtentScrollBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
|
||||
: _contentExtent = contentExtent, _containerExtent = containerExtent;
|
||||
|
||||
double _contentExtent;
|
||||
/// The linear extent of the content inside the scrollable widget
|
||||
double get contentExtent => _contentExtent;
|
||||
double _contentExtent;
|
||||
|
||||
double _containerExtent;
|
||||
/// The linear extent of the exterior of the scrollable widget
|
||||
double get containerExtent => _containerExtent;
|
||||
double _containerExtent;
|
||||
|
||||
/// Returns the new scrollOffset.
|
||||
/// Update either content or container extent (or both)
|
||||
///
|
||||
/// The scrollOffset parameter is the scroll offset of the widget before the
|
||||
/// change in extent. Returns the new scroll offset of the widget after the
|
||||
/// change in extent.
|
||||
double updateExtents({
|
||||
double contentExtent,
|
||||
double containerExtent,
|
||||
@ -39,10 +51,14 @@ abstract class ExtentScrollBehavior extends ScrollBehavior {
|
||||
return scrollOffset.clamp(minScrollOffset, maxScrollOffset);
|
||||
}
|
||||
|
||||
/// The minimum value the scroll offset can obtain
|
||||
double get minScrollOffset;
|
||||
|
||||
/// The maximum value the scroll offset can obatin
|
||||
double get maxScrollOffset;
|
||||
}
|
||||
|
||||
/// A scroll behavior that prevents the user from exeeding scroll bounds
|
||||
class BoundedBehavior extends ExtentScrollBehavior {
|
||||
BoundedBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
|
||||
: super(contentExtent: contentExtent, containerExtent: containerExtent);
|
||||
@ -55,6 +71,7 @@ class BoundedBehavior extends ExtentScrollBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
/// A scroll behavior that does not prevent the user from exeeding scroll bounds
|
||||
class UnboundedBehavior extends ExtentScrollBehavior {
|
||||
UnboundedBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
|
||||
: super(contentExtent: contentExtent, containerExtent: containerExtent);
|
||||
@ -74,19 +91,20 @@ class UnboundedBehavior extends ExtentScrollBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
Simulation createDefaultScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) {
|
||||
Simulation _createDefaultScrollSimulation(double position, double velocity, double minScrollOffset, double maxScrollOffset) {
|
||||
double velocityPerSecond = velocity * _kSecondsPerMillisecond;
|
||||
SpringDescription spring = new SpringDescription.withDampingRatio(
|
||||
mass: 1.0, springConstant: 170.0, ratio: 1.1);
|
||||
return new ScrollSimulation(position, velocityPerSecond, minScrollOffset, maxScrollOffset, spring, _kScrollDrag);
|
||||
}
|
||||
|
||||
/// A scroll behavior that lets the user scroll beyond the scroll bounds with some resistance
|
||||
class OverscrollBehavior extends BoundedBehavior {
|
||||
OverscrollBehavior({ double contentExtent: 0.0, double containerExtent: 0.0 })
|
||||
: super(contentExtent: contentExtent, containerExtent: containerExtent);
|
||||
|
||||
Simulation release(double position, double velocity) {
|
||||
return createDefaultScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset);
|
||||
return _createDefaultScrollSimulation(position, velocity, minScrollOffset, maxScrollOffset);
|
||||
}
|
||||
|
||||
double applyCurve(double scrollOffset, double scrollDelta) {
|
||||
@ -106,6 +124,7 @@ class OverscrollBehavior extends BoundedBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
/// A scroll behavior that lets the user scroll beyond the scroll bounds only when the bounds are disjoint
|
||||
class OverscrollWhenScrollableBehavior extends OverscrollBehavior {
|
||||
bool get isScrollable => contentExtent > containerExtent;
|
||||
|
||||
|
||||
@ -8,10 +8,14 @@ import 'package:newton/newton.dart';
|
||||
|
||||
import 'package:sky/src/animation/animated_simulation.dart';
|
||||
|
||||
// Simple 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 {
|
||||
final double _durationInSeconds;
|
||||
|
||||
/// The initial value of the simulation
|
||||
final double begin;
|
||||
|
||||
/// The terminal value of the simulation
|
||||
final double end;
|
||||
|
||||
TweenSimulation(Duration duration, this.begin, this.end) :
|
||||
@ -32,14 +36,15 @@ class TweenSimulation extends Simulation {
|
||||
bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
|
||||
}
|
||||
|
||||
/// A timeline for an animation
|
||||
class Timeline {
|
||||
Timeline(Function onTick) : _onTick = onTick {
|
||||
_animation = new AnimatedSimulation(_tick);
|
||||
Timeline(Function onTick) {
|
||||
_animation = new AnimatedSimulation(onTick);
|
||||
}
|
||||
|
||||
final Function _onTick;
|
||||
AnimatedSimulation _animation;
|
||||
|
||||
/// The current value of the timeline
|
||||
double get value => _animation.value.clamp(0.0, 1.0);
|
||||
void set value(double newValue) {
|
||||
assert(newValue != null && newValue >= 0.0 && newValue <= 1.0);
|
||||
@ -47,6 +52,7 @@ class Timeline {
|
||||
_animation.value = newValue;
|
||||
}
|
||||
|
||||
/// Whether the timeline is currently animating
|
||||
bool get isAnimating => _animation.isAnimating;
|
||||
|
||||
Future _start({
|
||||
@ -59,22 +65,23 @@ class Timeline {
|
||||
return _animation.start(new TweenSimulation(duration, begin, end));
|
||||
}
|
||||
|
||||
/// Animate value of the timeline to the given target over the given duration
|
||||
///
|
||||
/// Returns a future that resolves when the timeline stops animating,
|
||||
/// typically when the timeline arives at the target value.
|
||||
Future animateTo(double target, { Duration duration }) {
|
||||
assert(duration > Duration.ZERO);
|
||||
return _start(duration: duration, begin: value, end: target);
|
||||
}
|
||||
|
||||
/// Stop animating the timeline
|
||||
void stop() {
|
||||
_animation.stop();
|
||||
}
|
||||
|
||||
// Give |simulation| control over the timeline.
|
||||
// Gives the given simulation control over the timeline
|
||||
Future fling(Simulation simulation) {
|
||||
stop();
|
||||
return _animation.start(simulation);
|
||||
}
|
||||
|
||||
void _tick(double newValue) {
|
||||
_onTick(value);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user