提交 d3817ff4 编写于 作者: M Matt Perry

When animating, use the same curve until it completes.

This ensures we don't run into discontinuities when reversing an
animation halfway through. I refactored AnimationValue to have knowledge
of the reverse curves and intervals.
上级 7b0856d8
......@@ -5,84 +5,96 @@
import "dart:sky";
import 'package:sky/animation/curves.dart';
import 'package:sky/animation/direction.dart';
import 'package:sky/base/lerp.dart';
export 'package:sky/animation/curves.dart' show Interval;
abstract class AnimatedVariable {
void setProgress(double t);
void setProgress(double t, Direction direction);
String toString();
}
class Interval {
final double start;
final double end;
abstract class CurvedVariable implements AnimatedVariable {
CurvedVariable({this.interval, this.reverseInterval, this.curve, this.reverseCurve});
Interval interval;
Interval reverseInterval;
Curve curve;
Curve reverseCurve;
double _transform(double t, Direction direction) {
Interval interval = _getInterval(direction);
if (interval != null)
t = interval.transform(t);
if (t == 1.0) // Or should we support inverse curves?
return t;
Curve curve = _getCurve(direction);
if (curve != null)
t = curve.transform(t);
return t;
}
double adjustTime(double t) {
return ((t - start) / (end - start)).clamp(0.0, 1.0);
Interval _getInterval(Direction direction) {
if (direction == Direction.forward || reverseInterval == null)
return interval;
return reverseInterval;
}
Interval(this.start, this.end) {
assert(start >= 0.0);
assert(start <= 1.0);
assert(end >= 0.0);
assert(end <= 1.0);
Curve _getCurve(Direction direction) {
if (direction == Direction.forward || reverseCurve == null)
return curve;
return reverseCurve;
}
}
class AnimatedValue<T extends dynamic> extends AnimatedVariable {
AnimatedValue(this.begin, { this.end, this.interval, this.curve: linear }) {
class AnimatedValue<T extends dynamic> extends CurvedVariable {
AnimatedValue(this.begin, { this.end, Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve) {
value = begin;
}
T value;
T begin;
T end;
Interval interval;
Curve curve;
void setProgress(double t) {
T lerp(double t) => begin + (end - begin) * t;
void setProgress(double t, Direction direction) {
if (end != null) {
double adjustedTime = interval == null ? t : interval.adjustTime(t);
if (adjustedTime == 1.0) {
value = end;
} else {
// TODO(mpcomplete): Reverse the timeline and curve.
value = begin + (end - begin) * curve.transform(adjustedTime);
}
t = _transform(t, direction);
value = (t == 1.0) ? end : lerp(t);
}
}
String toString() => 'AnimatedValue(begin=$begin, end=$end, value=$value)';
}
class AnimatedList extends AnimatedVariable {
class AnimatedList extends CurvedVariable {
List<AnimatedVariable> variables;
Interval interval;
AnimatedList(this.variables, { this.interval });
AnimatedList(this.variables, { Interval interval, Curve curve, Curve reverseCurve })
: super(interval: interval, curve: curve, reverseCurve: reverseCurve);
void setProgress(double t) {
double adjustedTime = interval == null ? t : interval.adjustTime(t);
void setProgress(double t, Direction direction) {
double adjustedTime = _transform(t, direction);
for (AnimatedVariable variable in variables)
variable.setProgress(adjustedTime);
variable.setProgress(adjustedTime, direction);
}
String toString() => 'AnimatedList([$variables])';
}
class AnimatedColorValue extends AnimatedValue<Color> {
AnimatedColorValue(Color begin, { Color end, Curve curve: linear })
AnimatedColorValue(Color begin, { Color end, Curve curve })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
value = lerpColor(begin, end, t);
}
Color lerp(double t) => lerpColor(begin, end, t);
}
class AnimatedRect extends AnimatedValue<Rect> {
AnimatedRect(Rect begin, { Rect end, Curve curve: linear })
AnimatedRect(Rect begin, { Rect end, Curve curve })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
value = lerpRect(begin, end, t);
}
Rect lerp(double t) => lerpRect(begin, end, t);
}
......@@ -5,10 +5,11 @@
import 'dart:async';
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/direction.dart';
import 'package:sky/animation/forces.dart';
import 'package:sky/animation/timeline.dart';
export 'package:sky/animation/forces.dart' show Direction;
export 'package:sky/animation/direction.dart' show Direction;
enum AnimationStatus {
dismissed, // stoped at 0
......@@ -39,6 +40,12 @@ class AnimationPerformance {
Direction _direction;
Direction get direction => _direction;
// This controls which curve we use for variables with different curves in
// the forward/reverse directions. Curve direction is only reset when we hit
// 0 or 1, to avoid discontinuities.
Direction _curveDirection;
Direction get curveDirection => _curveDirection;
// If non-null, animate with this force instead of a tween animation.
Force attachedForce;
......@@ -74,6 +81,10 @@ class AnimationPerformance {
AnimationStatus.reverse;
}
void updateVariable(AnimatedVariable variable) {
variable.setProgress(progress, curveDirection);
}
Future play([Direction direction = Direction.forward]) {
_direction = direction;
return resume();
......@@ -136,6 +147,13 @@ class AnimationPerformance {
_lastStatus = currentStatus;
}
void _updateCurveDirection() {
if (status != _lastStatus) {
if (_lastStatus == AnimationStatus.dismissed || _lastStatus == AnimationStatus.completed)
_curveDirection = direction;
}
}
Future _animateTo(double target) {
Duration remainingDuration = duration * (target - timeline.value).abs();
timeline.stop();
......@@ -145,8 +163,9 @@ class AnimationPerformance {
}
void _tick(double t) {
_updateCurveDirection();
if (variable != null)
variable.setProgress(t);
variable.setProgress(t, curveDirection);
_notifyListeners();
_checkStatusChanged();
}
......
......@@ -21,6 +21,22 @@ class Linear implements Curve {
}
}
class Interval implements Curve {
final double start;
final double end;
Interval(this.start, this.end) {
assert(start >= 0.0);
assert(start <= 1.0);
assert(end >= 0.0);
assert(end <= 1.0);
}
double transform(double t) {
return ((t - start) / (end - start)).clamp(0.0, 1.0);
}
}
class ParabolicFall implements Curve {
const ParabolicFall();
......
// 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.
enum Direction {
forward,
reverse
}
......@@ -3,12 +3,7 @@
// found in the LICENSE file.
import 'package:newton/newton.dart';
// TODO(mpcomplete): This doesn't belong here.
enum Direction {
forward,
reverse
}
import 'package:sky/animation/direction.dart';
// Base class for creating Simulations for the animation Timeline.
abstract class Force {
......
......@@ -44,15 +44,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
void set value(bool value) {
if (value == _value) return;
_value = value;
// TODO(abarth): Setting the curve on the position means there's a
// discontinuity when we reverse the timeline.
if (value) {
_position.curve = easeIn;
_performance.play();
} else {
_position.curve = easeOut;
_performance.reverse();
}
performance.play(value ? Direction.forward : Direction.reverse);
}
ValueChanged _onChanged;
......@@ -63,7 +55,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
}
final AnimatedValue<double> _position =
new AnimatedValue<double>(0.0, end: 1.0);
new AnimatedValue<double>(0.0, end: 1.0, curve: easeIn, reverseCurve: easeOut);
AnimatedValue<double> get position => _position;
AnimationPerformance _performance;
......
......@@ -16,35 +16,23 @@ class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> {
AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
// TODO(abarth): We should lerp the BoxConstraints.
value = end;
}
// TODO(abarth): We should lerp the BoxConstraints.
BoxConstraints lerp(double t) => end;
}
class AnimatedBoxDecorationValue extends AnimatedValue<BoxDecoration> {
AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
if (t == 1.0) {
value = end;
return;
}
value = lerpBoxDecoration(begin, end, t);
}
BoxDecoration lerp(double t) => lerpBoxDecoration(begin, end, t);
}
class AnimatedEdgeDimsValue extends AnimatedValue<EdgeDims> {
AnimatedEdgeDimsValue(EdgeDims begin, { EdgeDims end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
if (t == 1.0) {
value = end;
return;
}
value = new EdgeDims(
EdgeDims lerp(double t) {
return new EdgeDims(
lerpNum(begin.top, end.top, t),
lerpNum(begin.right, end.right, t),
lerpNum(begin.bottom, end.bottom, t),
......@@ -57,17 +45,13 @@ class AnimatedMatrix4Value extends AnimatedValue<Matrix4> {
AnimatedMatrix4Value(Matrix4 begin, { Matrix4 end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setProgress(double t) {
if (t == 1.0) {
value = end;
return;
}
Matrix4 lerp(double t) {
// TODO(mpcomplete): Animate the full matrix. Will animating the cells
// separately work?
Vector3 beginT = begin.getTranslation();
Vector3 endT = end.getTranslation();
Vector3 lerpT = beginT*(1.0-t) + endT*t;
value = new Matrix4.identity()..translate(lerpT);
return new Matrix4.identity()..translate(lerpT);
}
}
......
......@@ -7,7 +7,6 @@ import 'dart:async';
import 'package:sky/animation/animated_value.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/animation/forces.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/focus.dart';
import 'package:sky/widgets/transitions.dart';
......
......@@ -50,7 +50,6 @@ class PopupMenu extends AnimatedComponent {
AnimatedValue<double> _width;
AnimatedValue<double> _height;
List<AnimatedValue<double>> _itemOpacities;
AnimatedList _animationList;
AnimationPerformance _performance;
void initState() {
......@@ -101,8 +100,9 @@ class PopupMenu extends AnimatedComponent {
..add(_width)
..add(_height)
..addAll(_itemOpacities);
_animationList = new AnimatedList(variables);
_performance.variable = _animationList;
AnimatedList list = new AnimatedList(variables)
..reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
_performance.variable = list;
}
void _updateBoxPainter() {
......@@ -124,14 +124,12 @@ class PopupMenu extends AnimatedComponent {
void _open() {
_animationList.interval = null;
_performance.play();
if (navigator != null)
navigator.pushState(this, (_) => _close());
}
void _close() {
_animationList.interval = new Interval(0.0, _kMenuCloseIntervalEnd);
_performance.reverse();
}
......
......@@ -98,7 +98,7 @@ class SlideTransition extends TransitionBase {
}
Widget build() {
position.setProgress(performance.progress);
performance.updateVariable(position);
Matrix4 transform = new Matrix4.identity()
..translate(position.value.x, position.value.y);
return new Transform(transform: transform, child: child);
......@@ -131,7 +131,7 @@ class FadeTransition extends TransitionBase {
}
Widget build() {
opacity.setProgress(performance.progress);
performance.updateVariable(opacity);
return new Opacity(opacity: opacity.value, child: child);
}
}
......@@ -162,7 +162,7 @@ class ColorTransition extends TransitionBase {
}
Widget build() {
color.setProgress(performance.progress);
performance.updateVariable(color);
return new DecoratedBox(
decoration: new BoxDecoration(backgroundColor: color.value),
child: child
......@@ -200,9 +200,9 @@ class SquashTransition extends TransitionBase {
Widget build() {
if (width != null)
width.setProgress(performance.progress);
performance.updateVariable(width);
if (height != null)
height.setProgress(performance.progress);
performance.updateVariable(height);
return new SizedBox(width: _maybe(width), height: _maybe(height), child: child);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册