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