Commit d1f0b86c authored by Adam Barth's avatar Adam Barth

Merge pull request #1323 from abarth/rm_simulation_stepper

Merge simulationStepper into AnimationController
parents 7ca8608a fc8ac4eb
......@@ -14,6 +14,5 @@ export 'src/animation/curves.dart';
export 'src/animation/forces.dart';
export 'src/animation/listener_helpers.dart';
export 'src/animation/scroll_behavior.dart';
export 'src/animation/simulation_stepper.dart';
export 'src/animation/ticker.dart';
export 'src/animation/tween.dart';
......@@ -3,23 +3,46 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' show Color, Size, Rect, VoidCallback, lerpDouble;
import 'dart:ui' show VoidCallback, lerpDouble;
import 'package:newton/newton.dart';
import 'animation.dart';
import 'curves.dart';
import 'forces.dart';
import 'listener_helpers.dart';
import 'simulation_stepper.dart';
import 'ticker.dart';
class AnimationController extends Animation<double>
with EagerListenerMixin, LocalPerformanceListenersMixin, LocalPerformanceStatusListenersMixin {
AnimationController({ this.duration, double value, this.debugLabel }) {
_timeline = new SimulationStepper(_tick);
if (value != null)
_timeline.value = value.clamp(0.0, 1.0);
AnimationController({
double value,
this.duration,
this.debugLabel,
this.lowerBound: 0.0,
this.upperBound: 1.0
}) {
_value = (value ?? lowerBound).clamp(lowerBound, upperBound);
_ticker = new Ticker(_tick);
}
AnimationController.unbounded({
double value: 0.0,
this.duration,
this.debugLabel
}) : lowerBound = double.NEGATIVE_INFINITY,
upperBound = double.INFINITY,
_value = value {
assert(value != null);
_ticker = new Ticker(_tick);
}
/// The value at which this animation is deemed to be dismissed.
final double lowerBound;
/// The value at which this animation is deemed to be completed.
final double upperBound;
/// A label that is used in the [toString] output. Intended to aid with
/// identifying animation controller instances in debug output.
final String debugLabel;
......@@ -32,27 +55,32 @@ class AnimationController extends Animation<double>
/// The length of time this animation should last.
Duration duration;
SimulationStepper _timeline;
AnimationDirection get direction => _direction;
AnimationDirection _direction = AnimationDirection.forward;
Ticker _ticker;
Simulation _simulation;
/// The progress of this animation along the timeline.
///
/// Note: Setting this value stops the current animation.
double get value => _timeline.value.clamp(0.0, 1.0);
void set value(double t) {
double get value => _value.clamp(lowerBound, upperBound);
double _value;
void set value(double newValue) {
assert(newValue != null);
stop();
_timeline.value = t.clamp(0.0, 1.0);
_value = newValue.clamp(lowerBound, upperBound);
notifyListeners();
_checkStatusChanged();
}
/// Whether this animation is currently animating in either the forward or reverse direction.
bool get isAnimating => _timeline.isAnimating;
bool get isAnimating => _ticker.isTicking;
AnimationStatus get status {
if (!isAnimating && value == 1.0)
if (!isAnimating && value == upperBound)
return AnimationStatus.completed;
if (!isAnimating && value == 0.0)
if (!isAnimating && value == lowerBound)
return AnimationStatus.dismissed;
return _direction == AnimationDirection.forward ?
AnimationStatus.forward :
......@@ -73,36 +101,35 @@ class AnimationController extends Animation<double>
/// Resumes this animation in the most recent direction.
Future resume() {
return _animateTo(_direction == AnimationDirection.forward ? 1.0 : 0.0);
return animateTo(_direction == AnimationDirection.forward ? upperBound : lowerBound);
}
/// Stops running this animation.
void stop() {
_timeline.stop();
}
/// Releases any resources used by this object.
///
/// Same as stop().
void dispose() {
stop();
_simulation = null;
_ticker.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.
Future fling({double velocity: 1.0, Force force}) {
Future fling({ double velocity: 1.0, Force force }) {
force ??= kDefaultSpringForce;
_direction = velocity < 0.0 ? AnimationDirection.reverse : AnimationDirection.forward;
return _timeline.animateWith(force.release(value, velocity));
return animateWith(force.release(value, velocity));
}
/// Starts running this animation in the forward direction, and
/// restarts the animation when it completes.
Future repeat({ double min: 0.0, double max: 1.0, Duration period }) {
period ??= duration;
return _timeline.animateWith(new _RepeatingSimulation(min, max, period));
return animateWith(new _RepeatingSimulation(min, max, period));
}
/// Drives the animation according to the given simulation.
Future animateWith(Simulation simulation) {
stop();
return _startSimulation(simulation);
}
AnimationStatus _lastStatus = AnimationStatus.dismissed;
......@@ -113,27 +140,70 @@ class AnimationController extends Animation<double>
_lastStatus = currentStatus;
}
Future _animateTo(double target) {
Duration remainingDuration = duration * (target - _timeline.value).abs();
_timeline.stop();
Future animateTo(double target, { Duration duration, Curve curve: Curves.linear }) {
Duration remainingDuration = (duration ?? this.duration) * (target - _value).abs();
stop();
if (remainingDuration == Duration.ZERO)
return new Future.value();
return _timeline.animateTo(target, duration: remainingDuration);
assert(remainingDuration > Duration.ZERO);
assert(!isAnimating);
return _startSimulation(new _TweenSimulation(_value, target, remainingDuration, curve));
}
Future _startSimulation(Simulation simulation) {
assert(simulation != null);
assert(!isAnimating);
_simulation = simulation;
_value = simulation.x(0.0);
return _ticker.start();
}
void _tick(double t) {
void _tick(Duration elapsed) {
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
_value = _simulation.x(elapsedInSeconds);
if (_simulation.isDone(elapsedInSeconds))
stop();
notifyListeners();
_checkStatusChanged();
}
String toStringDetails() {
String paused = _timeline.isAnimating ? '' : '; paused';
String paused = isAnimating ? '' : '; paused';
String label = debugLabel == null ? '' : '; for $debugLabel';
String more = '${super.toStringDetails()} ${value.toStringAsFixed(3)}';
return '$more$paused$label';
}
}
class _TweenSimulation extends Simulation {
_TweenSimulation(this._begin, this._end, Duration duration, this._curve)
: _durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
assert(_durationInSeconds > 0.0);
assert(_begin != null);
assert(_end != null);
}
final double _durationInSeconds;
final double _begin;
final double _end;
final Curve _curve;
double x(double timeInSeconds) {
assert(timeInSeconds >= 0.0);
double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
if (t == 0.0)
return _begin;
else if (t == 1.0)
return _end;
else
return _begin + (_end - _begin) * _curve.transform(t);
}
double dx(double timeInSeconds) => 1.0;
bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}
class _RepeatingSimulation extends Simulation {
_RepeatingSimulation(this.min, this.max, Duration period)
: _periodInSeconds = period.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:newton/newton.dart';
import 'curves.dart';
import 'ticker.dart';
/// A simulation that varies from begin to end over duration using curve.
class _TweenSimulation extends Simulation {
_TweenSimulation(this._begin, this._end, Duration duration, this._curve)
: _durationInSeconds = duration.inMicroseconds / Duration.MICROSECONDS_PER_SECOND {
assert(_durationInSeconds > 0.0);
assert(_begin != null);
assert(_end != null);
}
final double _durationInSeconds;
final double _begin;
final double _end;
final Curve _curve;
double x(double timeInSeconds) {
assert(timeInSeconds >= 0.0);
double t = (timeInSeconds / _durationInSeconds).clamp(0.0, 1.0);
if (t == 0.0)
return _begin;
else if (t == 1.0)
return _end;
else
return _begin + (_end - _begin) * _curve.transform(t);
}
double dx(double timeInSeconds) => 1.0;
bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
}
typedef TimelineCallback(double value);
/// Steps a simulation one per frame
class SimulationStepper {
SimulationStepper(TimelineCallback onTick) : _onTick = onTick {
_ticker = new Ticker(_tick);
}
final TimelineCallback _onTick;
Ticker _ticker;
Simulation _simulation;
/// The current value of the timeline.
double get value => _value;
double _value = 0.0;
void set value(double newValue) {
assert(newValue != null);
assert(!isAnimating);
_value = newValue;
_onTick(_value);
}
/// Whether the timeline is currently animating.
bool get isAnimating => _ticker.isTicking;
/// Animates 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, Curve curve: Curves.linear }) {
assert(duration > Duration.ZERO);
assert(!isAnimating);
return _start(new _TweenSimulation(value, target, duration, curve));
}
/// Gives the given simulation control over the timeline.
Future animateWith(Simulation simulation) {
stop();
return _start(simulation);
}
/// Starts 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(!isAnimating);
_simulation = simulation;
_value = simulation.x(0.0);
return _ticker.start();
}
/// Stops animating the timeline.
void stop() {
_simulation = null;
_ticker.stop();
}
void _tick(Duration elapsed) {
double elapsedInSeconds = elapsed.inMicroseconds.toDouble() / Duration.MICROSECONDS_PER_SECOND;
_value = _simulation.x(elapsedInSeconds);
if (_simulation.isDone(elapsedInSeconds))
stop();
_onTick(_value);
}
}
......@@ -109,11 +109,11 @@ abstract class Scrollable extends StatefulComponent {
abstract class ScrollableState<T extends Scrollable> extends State<T> {
void initState() {
super.initState();
_animation = new SimulationStepper(_setScrollOffset);
_controller = new AnimationController.unbounded()..addListener(_handleAnimationChanged);
_scrollOffset = PageStorage.of(context)?.readState(context) ?? config.initialScrollOffset ?? 0.0;
}
SimulationStepper _animation;
AnimationController _controller;
double get scrollOffset => _scrollOffset;
double _scrollOffset;
......@@ -181,9 +181,9 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
Widget buildContent(BuildContext context);
Future _animateTo(double newScrollOffset, Duration duration, Curve curve) {
_animation.stop();
_animation.value = scrollOffset;
return _animation.animateTo(newScrollOffset, duration: duration, curve: curve);
_controller.stop();
_controller.value = scrollOffset;
return _controller.animateTo(newScrollOffset, duration: duration, curve: curve);
}
bool _scrollOffsetIsInBounds(double offset) {
......@@ -240,18 +240,22 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
Future _startToEndAnimation(Offset scrollVelocity) {
double velocity = scrollDirectionVelocity(scrollVelocity);
_animation.stop();
_controller.stop();
Simulation simulation = _createSnapSimulation(velocity) ?? _createFlingSimulation(velocity);
if (simulation == null)
return new Future.value();
return _animation.animateWith(simulation);
return _controller.animateWith(simulation);
}
void dispose() {
_animation.stop();
_controller.stop();
super.dispose();
}
void _handleAnimationChanged() {
_setScrollOffset(_controller.value);
}
void _setScrollOffset(double newScrollOffset) {
if (_scrollOffset == newScrollOffset)
return;
......@@ -268,7 +272,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return new Future.value();
if (duration == null) {
_animation.stop();
_controller.stop();
_setScrollOffset(newScrollOffset);
return new Future.value();
}
......@@ -284,7 +288,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
Future fling(Offset scrollVelocity) {
if (scrollVelocity != Offset.zero)
return _startToEndAnimation(scrollVelocity);
if (!_animation.isAnimating)
if (!_controller.isAnimating)
return settleScrollOffset();
return new Future.value();
}
......@@ -310,7 +314,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
void _handlePointerDown(_) {
_animation.stop();
_controller.stop();
}
void _handleDragStart(_) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment