提交 0cac7098 编写于 作者: M Matt Perry

Add transitions.dart for common animation transitions.

Use those in SnackBar, Drawer, navigator instead of AnimatedContainer's
intentions.
上级 e719855b
......@@ -145,7 +145,8 @@ class AnimationPerformance {
}
void _tick(double t) {
variable.setProgress(t);
if (variable != null)
variable.setProgress(t);
_notifyListeners();
_checkStatusChanged();
}
......
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:sky' as sky;
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/forces.dart';
import 'package:sky/theme/shadows.dart';
......@@ -15,6 +16,7 @@ import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/navigator.dart';
import 'package:sky/widgets/scrollable.dart';
import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/transitions.dart';
export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
......@@ -57,17 +59,11 @@ class Drawer extends StatefulComponent {
DrawerStatusChangedCallback onStatusChanged;
Navigator navigator;
SlideInIntention _intention;
ColorTransitionIntention _maskColorIntention;
AnimationPerformance get _performance => _intention.performance;
AnimationPerformance _performance;
void initState() {
_intention = new SlideInIntention(
duration: _kBaseSettleDuration, start: _kClosedPosition, end: _kOpenPosition);
_maskColorIntention = new ColorTransitionIntention(
performance: _intention.performance, start: colors.transparent, end: const Color(0x7F000000));
_performance = new AnimationPerformance(duration: _kBaseSettleDuration);
_performance.addStatusListener(_onStatusChanged);
// Use a spring force for animating the drawer. We can't use curves for
// this because we need a linear curve in order to track the user's finger
// while dragging.
......@@ -87,27 +83,27 @@ class Drawer extends StatefulComponent {
Widget build() {
var mask = new Listener(
child: new AnimatedContainer(
intentions: [_maskColorIntention],
tag: showing
child: new ColorTransition(
performance: _performance,
direction: showing ? Direction.forward : Direction.reverse,
color: new AnimatedColorValue(colors.transparent, end: const Color(0x7F000000))
),
onGestureTap: handleMaskTap
);
Widget content = new AnimatedContainer(
intentions: [
_intention,
// TODO(mpcomplete): it should be easier to override some intentions,
// and have those you don't care about revert to a sensible default.
new ImplicitlySyncDecorationIntention(_kThemeChangeDuration),
new ImplicitlySyncWidthIntention(_kThemeChangeDuration),
],
tag: showing,
decoration: new BoxDecoration(
backgroundColor: Theme.of(this).canvasColor,
boxShadow: shadows[level]),
width: _kWidth,
child: new ScrollableBlock(children)
Widget content = new SlideIn(
performance: _performance,
direction: showing ? Direction.forward : Direction.reverse,
position: new AnimatedValue<Point>(_kClosedPosition, end: _kOpenPosition),
onDismissed: _onDismissed,
child: new AnimatedContainer(
intentions: implicitlySyncFieldsIntention(const Duration(milliseconds: 200)),
decoration: new BoxDecoration(
backgroundColor: Theme.of(this).canvasColor,
boxShadow: shadows[level]),
width: _kWidth,
child: new ScrollableBlock(children)
)
);
return new Listener(
......@@ -120,6 +116,10 @@ class Drawer extends StatefulComponent {
);
}
void _onDismissed() {
_onStatusChanged(AnimationStatus.dismissed);
}
void _onStatusChanged(AnimationStatus status) {
scheduleMicrotask(() {
if (status == AnimationStatus.dismissed &&
......
......@@ -11,6 +11,7 @@ import 'package:sky/animation/forces.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/focus.dart';
import 'package:sky/widgets/transitions.dart';
import 'package:vector_math/vector_math.dart';
typedef Widget RouteBuilder(Navigator navigator, RouteBase route);
......@@ -66,76 +67,39 @@ class RouteState extends RouteBase {
// and support multiple transition types
const Duration _kTransitionDuration = const Duration(milliseconds: 150);
const Point _kTransitionStartPoint = const Point(0.0, 75.0);
class Transition extends AnimatedComponent {
class Transition extends TransitionBase {
Transition({
Key key,
this.content,
this.direction,
this.onDismissed,
this.onCompleted,
Widget child,
Direction direction,
Function onDismissed,
Function onCompleted,
this.interactive
}): super(key: key);
Widget content;
Direction direction;
}): super(key: key,
child: child,
duration: _kTransitionDuration,
direction: direction,
onDismissed: onDismissed,
onCompleted: onCompleted);
bool interactive;
Function onDismissed;
Function onCompleted;
AnimatedValue<Point> _position;
AnimatedValue<double> _opacity;
AnimationPerformance _performance;
void initState() {
_position = new AnimatedValue<Point>(
_kTransitionStartPoint,
end: Point.origin,
curve: easeOut
);
_opacity = new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut);
_performance = new AnimationPerformance(duration: _kTransitionDuration)
..variable = new AnimatedList([_position, _opacity]);
if (direction == Direction.reverse)
_performance.progress = 1.0;
_performance.addStatusListener(_checkStatusChanged);
watch(_performance);
_start();
}
void _start() {
_performance.play(direction);
}
void syncFields(Transition source) {
content = source.content;
interactive = source.interactive;
onDismissed = source.onDismissed;
if (direction != source.direction) {
direction = source.direction;
_start();
}
super.syncFields(source);
}
void _checkStatusChanged(AnimationStatus status) {
if (_performance.isDismissed) {
if (onDismissed != null)
onDismissed();
} else if (_performance.isCompleted) {
if (onCompleted != null)
onCompleted();
}
}
Widget build() {
Matrix4 transform = new Matrix4.identity()
..translate(_position.value.x, _position.value.y);
// TODO(jackson): Hit testing should ignore transform
// TODO(jackson): Block input unless content is interactive
return new Transform(
transform: transform,
child: new Opacity(
opacity: _opacity.value,
child: content
return new SlideIn(
performance: performance,
direction: direction,
position: new AnimatedValue<Point>(_kTransitionStartPoint, end: Point.origin, curve: easeOut),
child: new FadeIn(
performance: performance,
direction: direction,
opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
child: child
)
);
}
......@@ -233,16 +197,16 @@ class Navigator extends StatefulComponent {
if (i + 1 < state.history.length && state.history[i + 1].fullyOpaque)
continue;
HistoryEntry historyEntry = state.history[i];
Widget content = historyEntry.route.build(this, historyEntry.route);
Widget child = historyEntry.route.build(this, historyEntry.route);
if (i == 0) {
visibleRoutes.add(content);
visibleRoutes.add(child);
continue;
}
if (content == null)
if (child == null)
continue;
Transition transition = new Transition(
key: new Key.fromObjectIdentity(historyEntry),
content: content,
child: child,
direction: (i <= state.historyIndex) ? Direction.forward : Direction.reverse,
interactive: (i == state.historyIndex),
onDismissed: () {
......
......@@ -4,15 +4,16 @@
import 'dart:async';
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/painting/text_style.dart';
import 'package:sky/theme/typography.dart' as typography;
import 'package:sky/widgets/animated_container.dart';
import 'package:sky/widgets/animation_intentions.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/default_text_style.dart';
import 'package:sky/widgets/material.dart';
import 'package:sky/widgets/theme.dart';
import 'package:sky/widgets/transitions.dart';
export 'package:sky/animation/animation_performance.dart' show AnimationStatus;
......@@ -39,8 +40,7 @@ class SnackBarAction extends Component {
);
}
}
class SnackBar extends StatefulComponent {
class SnackBar extends Component {
SnackBar({
Key key,
......@@ -57,25 +57,9 @@ class SnackBar extends StatefulComponent {
bool showing;
SnackBarStatusChangedCallback onStatusChanged;
SlideInIntention _intention;
void initState() {
_intention = new SlideInIntention(duration: _kSlideInDuration,
start: const Point(0.0, 50.0),
end: Point.origin);
_intention.performance.addStatusListener(_onStatusChanged);
}
void syncFields(SnackBar source) {
content = source.content;
actions = source.actions;
onStatusChanged = source.onStatusChanged;
showing = source.showing;
}
void _onStatusChanged(AnimationStatus status) {
void _onDismissed() {
if (onStatusChanged != null)
scheduleMicrotask(() { onStatusChanged(status); });
scheduleMicrotask(() { onStatusChanged(AnimationStatus.dismissed); });
}
Widget build() {
......@@ -91,9 +75,12 @@ class SnackBar extends StatefulComponent {
)
]..addAll(actions);
return new AnimatedContainer(
intentions: [_intention],
tag: showing,
return new SlideIn(
duration: _kSlideInDuration,
direction: showing ? Direction.forward : Direction.reverse,
position: new AnimatedValue<Point>(const Point(0.0, 50.0),
end: Point.origin),
onDismissed: _onDismissed,
child: new Material(
level: 2,
color: const Color(0xFF323232),
......
// 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 'package:sky/widgets/animated_component.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/animated_value.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/basic.dart';
import 'package:vector_math/vector_math.dart';
abstract class TransitionBase extends AnimatedComponent {
TransitionBase({
Key key,
this.child,
this.direction,
this.duration,
this.performance,
this.onDismissed,
this.onCompleted
}) : super(key: key);
Widget child;
Direction direction;
Duration duration;
AnimationPerformance performance;
Function onDismissed;
Function onCompleted;
void initState() {
if (performance == null) {
assert(duration != null);
performance = new AnimationPerformance(duration: duration);
}
if (direction == Direction.reverse)
performance.progress = 1.0;
performance.addStatusListener(_checkStatusChanged);
watch(performance);
_start();
}
void syncFields(TransitionBase source) {
child = source.child;
onCompleted = source.onCompleted;
onDismissed = source.onDismissed;
duration = source.duration;
if (direction != source.direction) {
direction = source.direction;
_start();
}
super.syncFields(source);
}
void _start() {
performance.play(direction);
}
void _checkStatusChanged(AnimationStatus status) {
if (performance.isDismissed) {
if (onDismissed != null)
onDismissed();
} else if (performance.isCompleted) {
if (onCompleted != null)
onCompleted();
}
}
Widget build();
}
class SlideIn extends TransitionBase {
// TODO(mpcomplete): this constructor is mostly boilerplate, passing values
// to super. Is there a simpler way?
SlideIn({
Key key,
this.position,
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);
AnimatedValue<Point> position;
void syncFields(SlideIn updated) {
position = updated.position;
super.syncFields(updated);
}
Widget build() {
position.setProgress(performance.progress);
Matrix4 transform = new Matrix4.identity()
..translate(position.value.x, position.value.y);
return new Transform(transform: transform, child: child);
}
}
class FadeIn extends TransitionBase {
FadeIn({
Key key,
this.opacity,
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);
AnimatedValue<double> opacity;
void syncFields(FadeIn updated) {
opacity = updated.opacity;
super.syncFields(updated);
}
Widget build() {
opacity.setProgress(performance.progress);
return new Opacity(opacity: opacity.value, child: child);
}
}
class ColorTransition extends TransitionBase {
ColorTransition({
Key key,
this.color,
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);
AnimatedColorValue color;
void syncFields(ColorTransition updated) {
color = updated.color;
super.syncFields(updated);
}
Widget build() {
color.setProgress(performance.progress);
return new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: color.value),
child: child
);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册