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

Commit 4d41c2e

Browse files
committed
Merge pull request #330 from mpcomplete/performance
Add an AnimationStatus to AnimationPerformance
2 parents f63c11c + df6ad4e commit 4d41c2e

File tree

5 files changed

+115
-96
lines changed

5 files changed

+115
-96
lines changed

sky/packages/sky/example/stocks/lib/stock_home.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class StockHome extends StatefulComponent {
2727
bool _isSearching = false;
2828
String _searchQuery;
2929

30-
SnackBarStatus _snackBarStatus = SnackBarStatus.inactive;
30+
AnimationStatus _snackBarStatus = AnimationStatus.dismissed;
3131
bool _isSnackBarShowing = false;
3232

3333
void _handleSearchBegin() {
@@ -59,28 +59,28 @@ class StockHome extends StatefulComponent {
5959
}
6060

6161
bool _drawerShowing = false;
62-
DrawerStatus _drawerStatus = DrawerStatus.inactive;
62+
AnimationStatus _drawerStatus = AnimationStatus.dismissed;
6363

6464
void _handleOpenDrawer() {
6565
setState(() {
6666
_drawerShowing = true;
67-
_drawerStatus = DrawerStatus.active;
67+
_drawerStatus = AnimationStatus.forward;
6868
});
6969
}
7070

71-
void _handleDrawerStatusChange(DrawerStatus status) {
71+
void _handleDrawerStatusChange(AnimationStatus status) {
7272
setState(() {
7373
_drawerStatus = status;
7474
});
7575
}
7676

7777
bool _menuShowing = false;
78-
PopupMenuStatus _menuStatus = PopupMenuStatus.inactive;
78+
AnimationStatus _menuStatus = AnimationStatus.dismissed;
7979

8080
void _handleMenuShow() {
8181
setState(() {
8282
_menuShowing = true;
83-
_menuStatus = PopupMenuStatus.active;
83+
_menuStatus = AnimationStatus.forward;
8484
});
8585
}
8686

@@ -90,7 +90,7 @@ class StockHome extends StatefulComponent {
9090
});
9191
}
9292

93-
void _handleMenuStatusChanged(PopupMenuStatus status) {
93+
void _handleMenuStatusChanged(AnimationStatus status) {
9494
setState(() {
9595
_menuStatus = status;
9696
});
@@ -112,7 +112,7 @@ class StockHome extends StatefulComponent {
112112
}
113113

114114
Drawer buildDrawer() {
115-
if (_drawerStatus == DrawerStatus.inactive)
115+
if (_drawerStatus == AnimationStatus.dismissed)
116116
return null;
117117
assert(_drawerShowing); // TODO(mpcomplete): this is always true
118118
return new Drawer(
@@ -246,7 +246,7 @@ class StockHome extends StatefulComponent {
246246
}
247247

248248
Widget buildSnackBar() {
249-
if (_snackBarStatus == SnackBarStatus.inactive)
249+
if (_snackBarStatus == AnimationStatus.dismissed)
250250
return null;
251251
return new SnackBar(
252252
showing: _isSnackBarShowing,
@@ -259,7 +259,7 @@ class StockHome extends StatefulComponent {
259259
void _handleStockPurchased() {
260260
setState(() {
261261
_isSnackBarShowing = true;
262-
_snackBarStatus = SnackBarStatus.active;
262+
_snackBarStatus = AnimationStatus.forward;
263263
});
264264
}
265265

@@ -272,7 +272,7 @@ class StockHome extends StatefulComponent {
272272
}
273273

274274
void addMenuToOverlays(List<Widget> overlays) {
275-
if (_menuStatus == PopupMenuStatus.inactive)
275+
if (_menuStatus == AnimationStatus.dismissed)
276276
return;
277277
overlays.add(new ModalOverlay(
278278
children: [new StockMenu(

sky/packages/sky/lib/animation/animation_performance.dart

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ import 'package:sky/animation/animated_value.dart';
88
import 'package:sky/animation/forces.dart';
99
import 'package:sky/animation/timeline.dart';
1010

11+
enum AnimationDirection {
12+
forward,
13+
reverse
14+
}
15+
16+
enum AnimationStatus {
17+
dismissed, // stoped at 0
18+
forward, // animating from 0 => 1
19+
reverse, // animating from 1 => 0
20+
completed, // stopped at 1
21+
}
22+
1123
// This class manages a "performance" - a collection of values that change
1224
// based on a timeline. For example, a performance may handle an animation
1325
// of a menu opening by sliding and fading in (changing Y value and opacity)
@@ -16,30 +28,49 @@ import 'package:sky/animation/timeline.dart';
1628
// manipulating |progress|, or |fling| the timeline causing a physics-based
1729
// simulation to take over the progression.
1830
class AnimationPerformance {
19-
AnimationPerformance() {
31+
AnimationPerformance({this.variable, this.duration}) {
2032
_timeline = new Timeline(_tick);
2133
}
2234

2335
AnimatedVariable variable;
24-
// TODO(mpcomplete): duration should be on a director.
2536
Duration duration;
2637

2738
// Advances from 0 to 1. On each tick, we'll update our variable's values.
2839
Timeline _timeline;
2940
Timeline get timeline => _timeline;
3041

42+
AnimationDirection _direction;
43+
AnimationDirection get direction => _direction;
44+
3145
double get progress => timeline.value;
3246
void set progress(double t) {
47+
// TODO(mpcomplete): should this affect |direction|?
3348
stop();
3449
timeline.value = t.clamp(0.0, 1.0);
50+
_checkStatusChanged();
3551
}
3652

37-
bool get isDismissed => progress == 0.0;
38-
bool get isCompleted => progress == 1.0;
53+
bool get isDismissed => status == AnimationStatus.dismissed;
54+
bool get isCompleted => status == AnimationStatus.completed;
3955
bool get isAnimating => timeline.isAnimating;
4056

41-
Future play() => _animateTo(1.0);
42-
Future reverse() => _animateTo(0.0);
57+
AnimationStatus get status {
58+
if (!isAnimating && progress == 1.0)
59+
return AnimationStatus.completed;
60+
if (!isAnimating && progress == 0.0)
61+
return AnimationStatus.dismissed;
62+
return direction == AnimationDirection.forward ?
63+
AnimationStatus.forward :
64+
AnimationStatus.reverse;
65+
}
66+
67+
Future play([AnimationDirection direction = AnimationDirection.forward]) {
68+
_direction = direction;
69+
return resume();
70+
}
71+
Future forward() => play(AnimationDirection.forward);
72+
Future reverse() => play(AnimationDirection.reverse);
73+
Future resume() => _animateTo(direction == AnimationDirection.forward ? 1.0 : 0.0);
4374

4475
void stop() {
4576
timeline.stop();
@@ -51,6 +82,9 @@ class AnimationPerformance {
5182
Future fling({double velocity: 1.0, Force force}) {
5283
if (force == null)
5384
force = kDefaultSpringForce;
85+
// This is an approximation - the force may not necessarily result in
86+
// animating the same direction as the initial velocity.
87+
_direction = velocity >= 0.0 ? AnimationDirection.forward : AnimationDirection.reverse;
5488
return timeline.fling(force.release(progress, velocity));
5589
}
5690

@@ -70,6 +104,27 @@ class AnimationPerformance {
70104
listener();
71105
}
72106

107+
final List<Function> _statusListeners = new List<Function>();
108+
109+
void addStatusListener(Function listener) {
110+
_statusListeners.add(listener);
111+
}
112+
113+
void removeStatusListener(Function listener) {
114+
_statusListeners.remove(listener);
115+
}
116+
117+
AnimationStatus _lastStatus = AnimationStatus.dismissed;
118+
void _checkStatusChanged() {
119+
AnimationStatus currentStatus = status;
120+
if (currentStatus != _lastStatus) {
121+
List<Function> localListeners = new List<Function>.from(_statusListeners);
122+
for (Function listener in localListeners)
123+
listener(currentStatus);
124+
}
125+
_lastStatus = currentStatus;
126+
}
127+
73128
Future _animateTo(double target) {
74129
double remainingDistance = (target - timeline.value).abs();
75130
timeline.stop();
@@ -81,5 +136,6 @@ class AnimationPerformance {
81136
void _tick(double t) {
82137
variable.setProgress(t);
83138
_notifyListeners();
139+
_checkStatusChanged();
84140
}
85141
}

sky/packages/sky/lib/widgets/drawer.dart

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import 'package:sky/widgets/scrollable_viewport.dart';
1515
import 'package:sky/widgets/theme.dart';
1616
import 'package:vector_math/vector_math.dart';
1717

18+
export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
19+
1820
// TODO(eseidel): Draw width should vary based on device size:
1921
// http://www.google.com/design/spec/layout/structure.html#structure-side-nav
2022

@@ -35,14 +37,7 @@ const Duration _kBaseSettleDuration = const Duration(milliseconds: 246);
3537
const Point _kOpenPosition = Point.origin;
3638
const Point _kClosedPosition = const Point(-_kWidth, 0.0);
3739

38-
typedef void DrawerStatusChangeHandler (bool showing);
39-
40-
enum DrawerStatus {
41-
active,
42-
inactive,
43-
}
44-
45-
typedef void DrawerStatusChangedCallback(DrawerStatus status);
40+
typedef void DrawerStatusChangedCallback(AnimationStatus status);
4641

4742
class Drawer extends AnimatedComponent {
4843
Drawer({
@@ -70,7 +65,7 @@ class Drawer extends AnimatedComponent {
7065
_performance = new AnimationPerformance()
7166
..duration = _kBaseSettleDuration
7267
..variable = new AnimatedList([_position, _maskColor])
73-
..addListener(_checkForStateChanged);
68+
..addStatusListener(_onStatusChanged);
7469
watch(_performance);
7570
if (showing)
7671
_show();
@@ -137,22 +132,16 @@ class Drawer extends AnimatedComponent {
137132

138133
double get xPosition => _position.value.x;
139134

140-
DrawerStatus _lastStatus;
141-
void _checkForStateChanged() {
142-
DrawerStatus status = _status;
143-
if (_lastStatus != null && status != _lastStatus) {
144-
if (status == DrawerStatus.inactive &&
145-
navigator != null &&
146-
navigator.currentRoute is RouteState &&
147-
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
148-
navigator.pop();
149-
if (onStatusChanged != null)
150-
onStatusChanged(status);
151-
}
152-
_lastStatus = status;
135+
void _onStatusChanged(AnimationStatus status) {
136+
if (status == AnimationStatus.dismissed &&
137+
navigator != null &&
138+
navigator.currentRoute is RouteState &&
139+
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
140+
navigator.pop();
141+
if (onStatusChanged != null)
142+
onStatusChanged(status);
153143
}
154144

155-
DrawerStatus get _status => _performance.isDismissed ? DrawerStatus.inactive : DrawerStatus.active;
156145
bool get _isMostlyClosed => xPosition <= -_kWidth/2;
157146

158147
void _settle() => _fling(_isMostlyClosed ? -1.0 : 1.0);

sky/packages/sky/lib/widgets/popup_menu.dart

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import 'package:sky/widgets/navigator.dart';
1616
import 'package:sky/widgets/popup_menu_item.dart';
1717
import 'package:sky/widgets/scrollable_viewport.dart';
1818

19+
export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
20+
1921
const Duration _kMenuDuration = const Duration(milliseconds: 300);
2022
double _kMenuCloseIntervalEnd = 2.0 / 3.0;
2123
const double _kMenuWidthStep = 56.0;
@@ -25,12 +27,7 @@ const double _kMenuMaxWidth = 5.0 * _kMenuWidthStep;
2527
const double _kMenuHorizontalPadding = 16.0;
2628
const double _kMenuVerticalPadding = 8.0;
2729

28-
enum PopupMenuStatus {
29-
active,
30-
inactive,
31-
}
32-
33-
typedef void PopupMenuStatusChangedCallback(PopupMenuStatus status);
30+
typedef void PopupMenuStatusChangedCallback(AnimationStatus status);
3431

3532
class PopupMenu extends AnimatedComponent {
3633

@@ -59,7 +56,7 @@ class PopupMenu extends AnimatedComponent {
5956
void initState() {
6057
_performance = new AnimationPerformance()
6158
..duration = _kMenuDuration
62-
..addListener(_checkForStateChanged);
59+
..addStatusListener(_onStatusChanged);
6360
_updateAnimationVariables();
6461
watch(_performance);
6562
_updateBoxPainter();
@@ -115,21 +112,14 @@ class PopupMenu extends AnimatedComponent {
115112
boxShadow: shadows[level]));
116113
}
117114

118-
PopupMenuStatus get _status => _opacity.value != 0.0 ? PopupMenuStatus.active : PopupMenuStatus.inactive;
119-
120-
PopupMenuStatus _lastStatus;
121-
void _checkForStateChanged() {
122-
PopupMenuStatus status = _status;
123-
if (_lastStatus != null && status != _lastStatus) {
124-
if (status == PopupMenuStatus.inactive &&
125-
navigator != null &&
126-
navigator.currentRoute is RouteState &&
127-
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
128-
navigator.pop();
129-
if (onStatusChanged != null)
130-
onStatusChanged(status);
131-
}
132-
_lastStatus = status;
115+
void _onStatusChanged(AnimationStatus status) {
116+
if (status == AnimationStatus.dismissed &&
117+
navigator != null &&
118+
navigator.currentRoute is RouteState &&
119+
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
120+
navigator.pop();
121+
if (onStatusChanged != null)
122+
onStatusChanged(status);
133123
}
134124

135125

0 commit comments

Comments
 (0)