Commit d89fb146 authored by Matt Perry's avatar Matt Perry

Push PopupMenu's animation code into its build function and use transitions.

Also add intervals and curves to AnimationPerformance, which affect all
variables used by the performance.
parent 870376da
...@@ -15,15 +15,15 @@ abstract class AnimatedVariable { ...@@ -15,15 +15,15 @@ abstract class AnimatedVariable {
String toString(); String toString();
} }
abstract class CurvedVariable implements AnimatedVariable { class AnimationTiming {
CurvedVariable({this.interval, this.reverseInterval, this.curve, this.reverseCurve}); AnimationTiming({this.interval, this.reverseInterval, this.curve, this.reverseCurve});
Interval interval; Interval interval;
Interval reverseInterval; Interval reverseInterval;
Curve curve; Curve curve;
Curve reverseCurve; Curve reverseCurve;
double _transform(double t, Direction direction) { double transform(double t, Direction direction) {
Interval interval = _getInterval(direction); Interval interval = _getInterval(direction);
if (interval != null) if (interval != null)
t = interval.transform(t); t = interval.transform(t);
...@@ -48,7 +48,7 @@ abstract class CurvedVariable implements AnimatedVariable { ...@@ -48,7 +48,7 @@ abstract class CurvedVariable implements AnimatedVariable {
} }
} }
class AnimatedValue<T extends dynamic> extends CurvedVariable { class AnimatedValue<T extends dynamic> extends AnimationTiming implements AnimatedVariable {
AnimatedValue(this.begin, { this.end, Interval interval, Curve curve, Curve reverseCurve }) AnimatedValue(this.begin, { this.end, Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve) { : super(interval: interval, curve: curve, reverseCurve: reverseCurve) {
value = begin; value = begin;
...@@ -62,7 +62,7 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable { ...@@ -62,7 +62,7 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable {
void setProgress(double t, Direction direction) { void setProgress(double t, Direction direction) {
if (end != null) { if (end != null) {
t = _transform(t, direction); t = transform(t, direction);
value = (t == 1.0) ? end : lerp(t); value = (t == 1.0) ? end : lerp(t);
} }
} }
...@@ -70,14 +70,14 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable { ...@@ -70,14 +70,14 @@ class AnimatedValue<T extends dynamic> extends CurvedVariable {
String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)'; String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)';
} }
class AnimatedList extends CurvedVariable { class AnimatedList extends AnimationTiming implements AnimatedVariable {
List<AnimatedVariable> variables; List<AnimatedVariable> variables;
AnimatedList(this.variables, { Interval interval, Curve curve, Curve reverseCurve }) AnimatedList(this.variables, { Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve); : super(interval: interval, curve: curve, reverseCurve: reverseCurve);
void setProgress(double t, Direction direction) { void setProgress(double t, Direction direction) {
double adjustedTime = _transform(t, direction); double adjustedTime = transform(t, direction);
for (AnimatedVariable variable in variables) for (AnimatedVariable variable in variables)
variable.setProgress(adjustedTime, direction); variable.setProgress(adjustedTime, direction);
} }
......
...@@ -46,6 +46,8 @@ class AnimationPerformance { ...@@ -46,6 +46,8 @@ class AnimationPerformance {
Direction _curveDirection; Direction _curveDirection;
Direction get curveDirection => _curveDirection; Direction get curveDirection => _curveDirection;
AnimationTiming timing;
// If non-null, animate with this force instead of a tween animation. // If non-null, animate with this force instead of a tween animation.
Force attachedForce; Force attachedForce;
...@@ -67,6 +69,10 @@ class AnimationPerformance { ...@@ -67,6 +69,10 @@ class AnimationPerformance {
_checkStatusChanged(); _checkStatusChanged();
} }
double get curvedProgress {
return timing != null ? timing.transform(progress, curveDirection) : progress;
}
bool get isDismissed => status == AnimationStatus.dismissed; bool get isDismissed => status == AnimationStatus.dismissed;
bool get isCompleted => status == AnimationStatus.completed; bool get isCompleted => status == AnimationStatus.completed;
bool get isAnimating => timeline.isAnimating; bool get isAnimating => timeline.isAnimating;
...@@ -82,7 +88,7 @@ class AnimationPerformance { ...@@ -82,7 +88,7 @@ class AnimationPerformance {
} }
void updateVariable(AnimatedVariable variable) { void updateVariable(AnimatedVariable variable) {
variable.setProgress(progress, curveDirection); variable.setProgress(curvedProgress, curveDirection);
} }
Future play([Direction direction = Direction.forward]) { Future play([Direction direction = Direction.forward]) {
...@@ -165,7 +171,7 @@ class AnimationPerformance { ...@@ -165,7 +171,7 @@ class AnimationPerformance {
void _tick(double t) { void _tick(double t) {
_updateCurveDirection(); _updateCurveDirection();
if (variable != null) if (variable != null)
variable.setProgress(t, curveDirection); variable.setProgress(curvedProgress, curveDirection);
_notifyListeners(); _notifyListeners();
_checkStatusChanged(); _checkStatusChanged();
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math; import 'dart:async';
import 'dart:sky' as sky; import 'dart:sky' as sky;
import 'package:sky/animation/animated_value.dart'; import 'package:sky/animation/animated_value.dart';
...@@ -10,11 +10,11 @@ import 'package:sky/animation/animation_performance.dart'; ...@@ -10,11 +10,11 @@ import 'package:sky/animation/animation_performance.dart';
import 'package:sky/painting/box_painter.dart'; import 'package:sky/painting/box_painter.dart';
import 'package:sky/theme/colors.dart'; import 'package:sky/theme/colors.dart';
import 'package:sky/theme/shadows.dart'; import 'package:sky/theme/shadows.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart'; import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/navigator.dart'; import 'package:sky/widgets/navigator.dart';
import 'package:sky/widgets/popup_menu_item.dart'; import 'package:sky/widgets/popup_menu_item.dart';
import 'package:sky/widgets/scrollable.dart'; import 'package:sky/widgets/scrollable.dart';
import 'package:sky/widgets/transitions.dart';
export 'package:sky/animation/animation_performance.dart' show AnimationStatus; export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
...@@ -29,7 +29,7 @@ const double _kMenuVerticalPadding = 8.0; ...@@ -29,7 +29,7 @@ const double _kMenuVerticalPadding = 8.0;
typedef void PopupMenuStatusChangedCallback(AnimationStatus status); typedef void PopupMenuStatusChangedCallback(AnimationStatus status);
class PopupMenu extends AnimatedComponent { class PopupMenu extends StatefulComponent {
PopupMenu({ PopupMenu({
Key key, Key key,
...@@ -46,63 +46,42 @@ class PopupMenu extends AnimatedComponent { ...@@ -46,63 +46,42 @@ class PopupMenu extends AnimatedComponent {
int level; int level;
Navigator navigator; Navigator navigator;
AnimatedValue<double> _opacity;
AnimatedValue<double> _width;
AnimatedValue<double> _height;
List<AnimatedValue<double>> _itemOpacities;
AnimationPerformance _performance; AnimationPerformance _performance;
void initState() { void initState() {
_performance = new AnimationPerformance() _performance = new AnimationPerformance()
..duration = _kMenuDuration ..duration = _kMenuDuration;
..addStatusListener(_onStatusChanged); _performance.timing = new AnimationTiming()
_updateAnimationVariables(); ..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
watch(_performance);
_updateBoxPainter(); _updateBoxPainter();
if (showing) if (showing)
_open(); _open();
} }
void syncFields(PopupMenu source) { void syncFields(PopupMenu source) {
if (showing != source.showing) { if (!showing && source.showing)
showing = source.showing;
if (showing)
_open(); _open();
else showing = source.showing;
_close();
}
onStatusChanged = source.onStatusChanged; onStatusChanged = source.onStatusChanged;
if (level != source.level) { if (level != source.level) {
level = source.level; level = source.level;
_updateBoxPainter(); _updateBoxPainter();
} }
if (items.length != source.items.length)
_updateAnimationVariables();
items = source.items; items = source.items;
navigator = source.navigator; navigator = source.navigator;
super.syncFields(source);
} }
void _updateAnimationVariables() { void _open() {
double unit = 1.0 / (items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade. if (navigator != null) {
_opacity = new AnimatedValue<double>(0.0, end: 1.0); scheduleMicrotask(() {
_width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit)); navigator.pushState(this, (_) => _close());
_height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length)); });
_itemOpacities = new List<AnimatedValue<double>>();
for (int i = 0; i < items.length; ++i) {
double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
_itemOpacities.add(new AnimatedValue<double>(
0.0, end: 1.0, interval: new Interval(start, end)));
} }
List<AnimatedVariable> variables = new List<AnimatedVariable>() }
..add(_opacity)
..add(_width) void _close() {
..add(_height) _performance.reverse();
..addAll(_itemOpacities);
AnimatedList list = new AnimatedList(variables)
..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
_performance.variable = list;
} }
void _updateBoxPainter() { void _updateBoxPainter() {
...@@ -112,44 +91,49 @@ class PopupMenu extends AnimatedComponent { ...@@ -112,44 +91,49 @@ class PopupMenu extends AnimatedComponent {
boxShadow: shadows[level])); boxShadow: shadows[level]));
} }
void _onStatusChanged(AnimationStatus status) { void _onDismissed() {
if (status == AnimationStatus.dismissed && if (navigator != null &&
navigator != null &&
navigator.currentRoute is RouteState && navigator.currentRoute is RouteState &&
(navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer (navigator.currentRoute as RouteState).owner == this) // TODO(ianh): remove cast once analyzer is cleverer
navigator.pop(); navigator.pop();
if (onStatusChanged != null) if (onStatusChanged != null)
onStatusChanged(status); onStatusChanged(AnimationStatus.dismissed);
}
void _open() {
_performance.play();
if (navigator != null)
navigator.pushState(this, (_) => _close());
}
void _close() {
_performance.reverse();
} }
BoxPainter _painter; BoxPainter _painter;
Widget build() { Widget build() {
int i = 0; double unit = 1.0 / (items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
List<Widget> children = new List.from(items.map((Widget item) { List<Widget> children = [];
return new Opacity(opacity: _itemOpacities[i++].value, child: item); for (int i = 0; i < items.length; ++i) {
})); double start = (i + 1) * unit;
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
children.add(new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
child: items[i]));
}
return new Opacity( final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
opacity: math.min(1.0, _opacity.value * 3.0), final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * items.length));
return new FadeTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
onDismissed: _onDismissed,
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)),
child: new Container( child: new Container(
margin: new EdgeDims.all(_kMenuMargin), margin: new EdgeDims.all(_kMenuMargin),
child: new CustomPaint( child: new BuilderTransition(
direction: showing ? Direction.forward : Direction.reverse,
performance: _performance,
variables: [width, height],
builder: () {
return new CustomPaint(
callback: (sky.Canvas canvas, Size size) { callback: (sky.Canvas canvas, Size size) {
double width = _width.value * size.width; double widthValue = width.value * size.width;
double height = _height.value * size.height; double heightValue = height.value * size.height;
_painter.paint(canvas, new Rect.fromLTWH(size.width - width, 0.0, width, height)); _painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
}, },
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: new BoxConstraints( constraints: new BoxConstraints(
...@@ -169,6 +153,8 @@ class PopupMenu extends AnimatedComponent { ...@@ -169,6 +153,8 @@ class PopupMenu extends AnimatedComponent {
) )
) )
) )
);
}
) )
) )
); );
......
...@@ -92,9 +92,9 @@ class SlideTransition extends TransitionBase { ...@@ -92,9 +92,9 @@ class SlideTransition extends TransitionBase {
AnimatedValue<Point> position; AnimatedValue<Point> position;
void syncFields(SlideTransition updated) { void syncFields(SlideTransition source) {
position = updated.position; position = source.position;
super.syncFields(updated); super.syncFields(source);
} }
Widget build() { Widget build() {
...@@ -125,9 +125,9 @@ class FadeTransition extends TransitionBase { ...@@ -125,9 +125,9 @@ class FadeTransition extends TransitionBase {
AnimatedValue<double> opacity; AnimatedValue<double> opacity;
void syncFields(FadeTransition updated) { void syncFields(FadeTransition source) {
opacity = updated.opacity; opacity = source.opacity;
super.syncFields(updated); super.syncFields(source);
} }
Widget build() { Widget build() {
...@@ -156,9 +156,9 @@ class ColorTransition extends TransitionBase { ...@@ -156,9 +156,9 @@ class ColorTransition extends TransitionBase {
AnimatedColorValue color; AnimatedColorValue color;
void syncFields(ColorTransition updated) { void syncFields(ColorTransition source) {
color = updated.color; color = source.color;
super.syncFields(updated); super.syncFields(source);
} }
Widget build() { Widget build() {
...@@ -192,10 +192,10 @@ class SquashTransition extends TransitionBase { ...@@ -192,10 +192,10 @@ class SquashTransition extends TransitionBase {
AnimatedValue<double> width; AnimatedValue<double> width;
AnimatedValue<double> height; AnimatedValue<double> height;
void syncFields(SquashTransition updated) { void syncFields(SquashTransition source) {
width = updated.width; width = source.width;
height = updated.height; height = source.height;
super.syncFields(updated); super.syncFields(source);
} }
Widget build() { Widget build() {
...@@ -206,3 +206,40 @@ class SquashTransition extends TransitionBase { ...@@ -206,3 +206,40 @@ class SquashTransition extends TransitionBase {
return new SizedBox(width: _maybe(width), height: _maybe(height), child: child); return new SizedBox(width: _maybe(width), height: _maybe(height), child: child);
} }
} }
typedef Widget BuilderFunction();
class BuilderTransition extends TransitionBase {
BuilderTransition({
Key key,
this.variables,
this.builder,
Duration duration,
AnimationPerformance performance,
Direction direction,
Function onDismissed,
Function onCompleted,
Widget child
}) : super(key: key,
duration: duration,
performance: performance,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted,
child: child);
List<AnimatedValue> variables;
BuilderFunction builder;
void syncFields(BuilderTransition source) {
variables = source.variables;
builder = source.builder;
super.syncFields(source);
}
Widget build() {
for (int i = 0; i < variables.length; ++i)
performance.updateVariable(variables[i]);
return builder();
}
}
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