Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit edd9b31

Browse files
committed
Merge pull request #3 from domokit/drawer.spring
Make Drawer animate using a spring force instead of a linear curve.
2 parents cf074be + 871eeb2 commit edd9b31

File tree

7 files changed

+38
-46
lines changed

7 files changed

+38
-46
lines changed

DEPS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ vars = {
3434
'cassowary_dart_revision': '7e5afc5b3956a18636d5b37b1dcba1705865564b',
3535
'collection_dart_revision': '79ebc6fc2dae581cb23ad50a5c600c1b7dd132f8',
3636
'crypto_dart_revision': 'd4558dea1639e5ad2a41d045265b8ece270c2d90',
37-
'newton_dart_revision': '26da04f0c441d005a6ecbf62ae047cd02ec9abc5',
37+
'newton_dart_revision': '9fbe5fbac809246f7ace4176feca13bdf731e393',
3838
'path_dart_revision': '2f3dcdec32011f1bc41194ae3640d6d9292a7096',
3939
'quiver_dart_revision': '6bab7dec34189eee579178eb16d3063c8ae69031',
4040
'source_span_dart_revision': '5c6c13f62fc111adaace3aeb4a38853d64481d06',

sky/sdk/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ dart_pkg("sky") {
1111
"lib/animation/animated_simulation.dart",
1212
"lib/animation/animation_performance.dart",
1313
"lib/animation/curves.dart",
14+
"lib/animation/forces.dart",
1415
"lib/animation/scroll_behavior.dart",
1516
"lib/animation/timeline.dart",
1617
"lib/assets/.gitignore",

sky/sdk/lib/animation/animation_performance.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:newton/newton.dart';
5+
import 'dart:async';
6+
67
import 'package:sky/animation/timeline.dart';
78
import 'package:sky/animation/curves.dart';
9+
import 'package:sky/animation/forces.dart';
810

911
abstract class AnimatedVariable {
1012
void setFraction(double t);
@@ -99,23 +101,20 @@ class AnimationPerformance {
99101
bool get isCompleted => progress == 1.0;
100102
bool get isAnimating => timeline.isAnimating;
101103

102-
void play() {
103-
_animateTo(1.0);
104-
}
105-
void reverse() {
106-
_animateTo(0.0);
107-
}
104+
Future play() => _animateTo(1.0);
105+
Future reverse() => _animateTo(0.0);
108106

109107
void stop() {
110108
timeline.stop();
111109
}
112110

113-
// Resume animating in a direction, with the given velocity.
114-
// TODO(mpcomplete): Allow user to specify the Simulation.
115-
void fling({double velocity: 1.0}) {
116-
Simulation simulation =
117-
timeline.defaultSpringSimulation(velocity: velocity);
118-
timeline.fling(simulation);
111+
// Flings the timeline with an optional force (defaults to a critically damped
112+
// spring) and initial velocity. Negative velocity causes the timeline to go
113+
// in reverse.
114+
Future fling({double velocity: 1.0, Force force}) {
115+
if (force == null)
116+
force = kDefaultSpringForce;
117+
return timeline.fling(force.release(progress, velocity));
119118
}
120119

121120
final List<Function> _listeners = new List<Function>();
@@ -134,11 +133,12 @@ class AnimationPerformance {
134133
listener();
135134
}
136135

137-
void _animateTo(double target) {
136+
Future _animateTo(double target) {
138137
double remainingDistance = (target - timeline.value).abs();
139138
timeline.stop();
140-
if (remainingDistance != 0.0)
141-
timeline.animateTo(target, duration: duration * remainingDistance);
139+
if (remainingDistance == 0.0)
140+
return new Future.value();
141+
return timeline.animateTo(target, duration: duration * remainingDistance);
142142
}
143143

144144
void _tick(double t) {

sky/sdk/lib/animation/scroll_behavior.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
import 'dart:math' as math;
66

77
import 'package:newton/newton.dart';
8+
import 'package:sky/animation/forces.dart';
89

910
const double _kSecondsPerMillisecond = 1000.0;
1011

11-
abstract class ScrollBehavior {
12+
abstract class ScrollBehavior extends Force {
1213
Simulation release(double position, double velocity) => null;
1314

1415
// Returns the new scroll offset.

sky/sdk/lib/animation/timeline.dart

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import 'dart:async';
77
import 'package:newton/newton.dart';
88
import 'package:sky/animation/animated_simulation.dart';
99

10-
const double _kEpsilon = 0.001;
11-
1210
// Simple simulation that linearly varies from |begin| to |end| over |duration|.
1311
class TweenSimulation extends Simulation {
1412
final double _durationInSeconds;
@@ -68,17 +66,6 @@ class Timeline {
6866
_animation.stop();
6967
}
7068

71-
static final SpringDescription _kDefaultSpringDesc =
72-
new SpringDescription.withDampingRatio(
73-
mass: 1.0, springConstant: 500.0, ratio: 1.0);
74-
75-
Simulation defaultSpringSimulation({double velocity: 0.0}) {
76-
// Target just past the 0 or 1 endpoint, because the animation will stop
77-
// once the Spring gets within the epsilon, and we want to stop at 0 or 1.
78-
double target = velocity < 0.0 ? -_kEpsilon : 1.0 + _kEpsilon;
79-
return new SpringSimulation(_kDefaultSpringDesc, value, target, velocity);
80-
}
81-
8269
// Give |simulation| control over the timeline.
8370
Future fling(Simulation simulation) {
8471
stop();

sky/sdk/lib/widgets/dismissable.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:vector_math/vector_math.dart';
1313
const Duration _kCardDismissFadeout = const Duration(milliseconds: 300);
1414
const double _kMinFlingVelocity = 700.0;
1515
const double _kMinFlingVelocityDelta = 400.0;
16+
const double _kFlingVelocityScale = 1.0/300.0;
1617
const double _kDismissCardThreshold = 0.6;
1718

1819
typedef void DismissedCallback();
@@ -129,7 +130,7 @@ class Dismissable extends AnimatedComponent {
129130
_dragUnderway = false;
130131
_dragX = event.velocityX.sign;
131132
_position.end = _activeCardDragEndPoint;
132-
_performance.fling(velocity: event.velocityX.abs() / _width);
133+
_performance.fling(velocity: event.velocityX.abs() * _kFlingVelocityScale);
133134
}
134135
}
135136

sky/sdk/lib/widgets/drawer.dart

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import 'dart:sky' as sky;
66

77
import 'package:sky/animation/animation_performance.dart';
8-
import 'package:sky/animation/curves.dart';
98
import 'package:sky/theme/shadows.dart';
109
import 'package:sky/theme/colors.dart' as colors;
1110
import 'package:sky/widgets/animated_component.dart';
@@ -30,14 +29,11 @@ import 'package:vector_math/vector_math.dart';
3029
// The right nav can vary depending on content.
3130

3231
const double _kWidth = 304.0;
33-
const double _kMinFlingVelocity = 1.2;
32+
const double _kMinFlingVelocity = 365.0;
33+
const double _kFlingVelocityScale = 1.0/300.0;
3434
const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
35-
// TODO(mpcomplete): The curve must be linear if we want the drawer to track
36-
// the user's finger. Odeon remedies this by attaching spring forces to the
37-
// initial timeline when animating (so it doesn't look linear).
3835
const Point _kOpenPosition = Point.origin;
3936
const Point _kClosedPosition = const Point(-_kWidth, 0.0);
40-
const Curve _kAnimationCurve = linear;
4137

4238
typedef void DrawerStatusChangeHandler (bool showing);
4339

@@ -69,7 +65,7 @@ class Drawer extends AnimatedComponent {
6965
AnimationPerformance _performance;
7066

7167
void initState() {
72-
_position = new AnimatedType<Point>(_kClosedPosition, end: _kOpenPosition, curve: _kAnimationCurve);
68+
_position = new AnimatedType<Point>(_kClosedPosition, end: _kOpenPosition);
7369
_maskColor = new AnimatedColor(colors.transparent, end: const Color(0x7F000000));
7470
_performance = new AnimationPerformance()
7571
..duration = _kBaseSettleDuration
@@ -95,11 +91,18 @@ class Drawer extends AnimatedComponent {
9591
void _show() {
9692
if (navigator != null)
9793
navigator.pushState(this, (_) => _performance.reverse());
98-
_performance.play();
94+
_fling(1.0);
9995
}
10096

10197
void _hide() {
102-
_performance.reverse();
98+
_fling(-1.0);
99+
}
100+
101+
// We fling the performance timeline instead of animating it to give it a
102+
// nice spring effect. We can't use curves for this because we need a linear
103+
// curve in order to track the user's finger while dragging.
104+
void _fling(double direction) {
105+
_performance.fling(velocity: direction.sign);
103106
}
104107

105108
Widget build() {
@@ -151,9 +154,9 @@ class Drawer extends AnimatedComponent {
151154
DrawerStatus get _status => _performance.isDismissed ? DrawerStatus.inactive : DrawerStatus.active;
152155
bool get _isMostlyClosed => xPosition <= -_kWidth/2;
153156

154-
void _settle() => _isMostlyClosed ? _performance.reverse() : _performance.play();
157+
void _settle() => _fling(_isMostlyClosed ? -1.0 : 1.0);
155158

156-
void handleMaskTap(_) => _performance.reverse();
159+
void handleMaskTap(_) => _fling(-1.0);
157160

158161
// TODO(mpcomplete): Figure out how to generalize these handlers on a
159162
// "PannableThingy" interface.
@@ -176,8 +179,7 @@ class Drawer extends AnimatedComponent {
176179
}
177180

178181
void handleFlingStart(event) {
179-
double velocityX = event.velocityX / _kWidth;
180-
if (velocityX.abs() >= _kMinFlingVelocity)
181-
_performance.fling(velocity: velocityX);
182+
if (event.velocityX.abs() >= _kMinFlingVelocity)
183+
_performance.fling(velocity: event.velocityX * _kFlingVelocityScale);
182184
}
183185
}

0 commit comments

Comments
 (0)