diff --git a/DEPS b/DEPS index 68fd41e1631fd..ddaf345762b78 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { 'cassowary_dart_revision': '7e5afc5b3956a18636d5b37b1dcba1705865564b', 'collection_dart_revision': '79ebc6fc2dae581cb23ad50a5c600c1b7dd132f8', 'crypto_dart_revision': 'd4558dea1639e5ad2a41d045265b8ece270c2d90', - 'newton_dart_revision': '26da04f0c441d005a6ecbf62ae047cd02ec9abc5', + 'newton_dart_revision': '9fbe5fbac809246f7ace4176feca13bdf731e393', 'path_dart_revision': '2f3dcdec32011f1bc41194ae3640d6d9292a7096', 'quiver_dart_revision': '6bab7dec34189eee579178eb16d3063c8ae69031', 'source_span_dart_revision': '5c6c13f62fc111adaace3aeb4a38853d64481d06', diff --git a/sky/sdk/BUILD.gn b/sky/sdk/BUILD.gn index 1e756f26c95b3..b78bbc25489f2 100644 --- a/sky/sdk/BUILD.gn +++ b/sky/sdk/BUILD.gn @@ -11,6 +11,7 @@ dart_pkg("sky") { "lib/animation/animated_simulation.dart", "lib/animation/animation_performance.dart", "lib/animation/curves.dart", + "lib/animation/forces.dart", "lib/animation/scroll_behavior.dart", "lib/animation/timeline.dart", "lib/assets/.gitignore", diff --git a/sky/sdk/lib/animation/animation_performance.dart b/sky/sdk/lib/animation/animation_performance.dart index 6864861f9f9f9..5c678c1418d87 100644 --- a/sky/sdk/lib/animation/animation_performance.dart +++ b/sky/sdk/lib/animation/animation_performance.dart @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:newton/newton.dart'; +import 'dart:async'; + import 'package:sky/animation/timeline.dart'; import 'package:sky/animation/curves.dart'; +import 'package:sky/animation/forces.dart'; abstract class AnimatedVariable { void setFraction(double t); @@ -99,23 +101,20 @@ class AnimationPerformance { bool get isCompleted => progress == 1.0; bool get isAnimating => timeline.isAnimating; - void play() { - _animateTo(1.0); - } - void reverse() { - _animateTo(0.0); - } + Future play() => _animateTo(1.0); + Future reverse() => _animateTo(0.0); void stop() { timeline.stop(); } - // Resume animating in a direction, with the given velocity. - // TODO(mpcomplete): Allow user to specify the Simulation. - void fling({double velocity: 1.0}) { - Simulation simulation = - timeline.defaultSpringSimulation(velocity: velocity); - timeline.fling(simulation); + // Flings the timeline with an optional force (defaults to a critically damped + // spring) and initial velocity. Negative velocity causes the timeline to go + // in reverse. + Future fling({double velocity: 1.0, Force force}) { + if (force == null) + force = kDefaultSpringForce; + return timeline.fling(force.release(progress, velocity)); } final List _listeners = new List(); @@ -134,11 +133,12 @@ class AnimationPerformance { listener(); } - void _animateTo(double target) { + Future _animateTo(double target) { double remainingDistance = (target - timeline.value).abs(); timeline.stop(); - if (remainingDistance != 0.0) - timeline.animateTo(target, duration: duration * remainingDistance); + if (remainingDistance == 0.0) + return new Future.value(); + return timeline.animateTo(target, duration: duration * remainingDistance); } void _tick(double t) { diff --git a/sky/sdk/lib/animation/scroll_behavior.dart b/sky/sdk/lib/animation/scroll_behavior.dart index b01713c888d05..2e8c2be7e85e8 100644 --- a/sky/sdk/lib/animation/scroll_behavior.dart +++ b/sky/sdk/lib/animation/scroll_behavior.dart @@ -5,10 +5,11 @@ import 'dart:math' as math; import 'package:newton/newton.dart'; +import 'package:sky/animation/forces.dart'; const double _kSecondsPerMillisecond = 1000.0; -abstract class ScrollBehavior { +abstract class ScrollBehavior extends Force { Simulation release(double position, double velocity) => null; // Returns the new scroll offset. diff --git a/sky/sdk/lib/animation/timeline.dart b/sky/sdk/lib/animation/timeline.dart index 607cb6d4ca9f3..680130d76bc43 100644 --- a/sky/sdk/lib/animation/timeline.dart +++ b/sky/sdk/lib/animation/timeline.dart @@ -7,8 +7,6 @@ import 'dart:async'; import 'package:newton/newton.dart'; import 'package:sky/animation/animated_simulation.dart'; -const double _kEpsilon = 0.001; - // Simple simulation that linearly varies from |begin| to |end| over |duration|. class TweenSimulation extends Simulation { final double _durationInSeconds; @@ -68,17 +66,6 @@ class Timeline { _animation.stop(); } - static final SpringDescription _kDefaultSpringDesc = - new SpringDescription.withDampingRatio( - mass: 1.0, springConstant: 500.0, ratio: 1.0); - - Simulation defaultSpringSimulation({double velocity: 0.0}) { - // Target just past the 0 or 1 endpoint, because the animation will stop - // once the Spring gets within the epsilon, and we want to stop at 0 or 1. - double target = velocity < 0.0 ? -_kEpsilon : 1.0 + _kEpsilon; - return new SpringSimulation(_kDefaultSpringDesc, value, target, velocity); - } - // Give |simulation| control over the timeline. Future fling(Simulation simulation) { stop(); diff --git a/sky/sdk/lib/widgets/dismissable.dart b/sky/sdk/lib/widgets/dismissable.dart index 0c4363e335435..a96612e649e77 100644 --- a/sky/sdk/lib/widgets/dismissable.dart +++ b/sky/sdk/lib/widgets/dismissable.dart @@ -13,6 +13,7 @@ import 'package:vector_math/vector_math.dart'; const Duration _kCardDismissFadeout = const Duration(milliseconds: 300); const double _kMinFlingVelocity = 700.0; const double _kMinFlingVelocityDelta = 400.0; +const double _kFlingVelocityScale = 1.0/300.0; const double _kDismissCardThreshold = 0.6; typedef void DismissedCallback(); @@ -129,7 +130,7 @@ class Dismissable extends AnimatedComponent { _dragUnderway = false; _dragX = event.velocityX.sign; _position.end = _activeCardDragEndPoint; - _performance.fling(velocity: event.velocityX.abs() / _width); + _performance.fling(velocity: event.velocityX.abs() * _kFlingVelocityScale); } } diff --git a/sky/sdk/lib/widgets/drawer.dart b/sky/sdk/lib/widgets/drawer.dart index 933f942f585e1..60f3682e8263a 100644 --- a/sky/sdk/lib/widgets/drawer.dart +++ b/sky/sdk/lib/widgets/drawer.dart @@ -5,7 +5,6 @@ import 'dart:sky' as sky; import 'package:sky/animation/animation_performance.dart'; -import 'package:sky/animation/curves.dart'; import 'package:sky/theme/shadows.dart'; import 'package:sky/theme/colors.dart' as colors; import 'package:sky/widgets/animated_component.dart'; @@ -30,14 +29,11 @@ import 'package:vector_math/vector_math.dart'; // The right nav can vary depending on content. const double _kWidth = 304.0; -const double _kMinFlingVelocity = 1.2; +const double _kMinFlingVelocity = 365.0; +const double _kFlingVelocityScale = 1.0/300.0; const Duration _kBaseSettleDuration = const Duration(milliseconds: 246); -// TODO(mpcomplete): The curve must be linear if we want the drawer to track -// the user's finger. Odeon remedies this by attaching spring forces to the -// initial timeline when animating (so it doesn't look linear). const Point _kOpenPosition = Point.origin; const Point _kClosedPosition = const Point(-_kWidth, 0.0); -const Curve _kAnimationCurve = linear; typedef void DrawerStatusChangeHandler (bool showing); @@ -69,7 +65,7 @@ class Drawer extends AnimatedComponent { AnimationPerformance _performance; void initState() { - _position = new AnimatedType(_kClosedPosition, end: _kOpenPosition, curve: _kAnimationCurve); + _position = new AnimatedType(_kClosedPosition, end: _kOpenPosition); _maskColor = new AnimatedColor(colors.transparent, end: const Color(0x7F000000)); _performance = new AnimationPerformance() ..duration = _kBaseSettleDuration @@ -95,11 +91,18 @@ class Drawer extends AnimatedComponent { void _show() { if (navigator != null) navigator.pushState(this, (_) => _performance.reverse()); - _performance.play(); + _fling(1.0); } void _hide() { - _performance.reverse(); + _fling(-1.0); + } + + // We fling the performance timeline instead of animating it to give it a + // nice spring effect. We can't use curves for this because we need a linear + // curve in order to track the user's finger while dragging. + void _fling(double direction) { + _performance.fling(velocity: direction.sign); } Widget build() { @@ -151,9 +154,9 @@ class Drawer extends AnimatedComponent { DrawerStatus get _status => _performance.isDismissed ? DrawerStatus.inactive : DrawerStatus.active; bool get _isMostlyClosed => xPosition <= -_kWidth/2; - void _settle() => _isMostlyClosed ? _performance.reverse() : _performance.play(); + void _settle() => _fling(_isMostlyClosed ? -1.0 : 1.0); - void handleMaskTap(_) => _performance.reverse(); + void handleMaskTap(_) => _fling(-1.0); // TODO(mpcomplete): Figure out how to generalize these handlers on a // "PannableThingy" interface. @@ -176,8 +179,7 @@ class Drawer extends AnimatedComponent { } void handleFlingStart(event) { - double velocityX = event.velocityX / _kWidth; - if (velocityX.abs() >= _kMinFlingVelocity) - _performance.fling(velocity: velocityX); + if (event.velocityX.abs() >= _kMinFlingVelocity) + _performance.fling(velocity: event.velocityX * _kFlingVelocityScale); } }