提交 d909155d 编写于 作者: A Adam Barth

Refactor Material animation to use AnimatedContainer

The idea is that AnimatedContainer is a drop-in replacement for Container that
provides implicit animations when its properties change.

R=mpcomplete@google.com
上级 18022ca6
......@@ -22,6 +22,22 @@ class Color {
bool operator ==(other) => other is Color && _value == other._value;
Color withAlpha(int a) {
return new Color.fromARGB(a, red, green, blue);
}
Color withRed(int r) {
return new Color.fromARGB(alpha, r, green, blue);
}
Color withGreen(int g) {
return new Color.fromARGB(alpha, red, g, blue);
}
Color withBlue(int b) {
return new Color.fromARGB(alpha, red, green, b);
}
int get hashCode => _value.hashCode;
String toString() => "Color(0x${_value.toRadixString(16).padLeft(8, '0')})";
}
......@@ -50,6 +50,7 @@ dart_pkg("sky") {
"lib/theme/typography.dart",
"lib/theme/view_configuration.dart",
"lib/widgets/animated_component.dart",
"lib/widgets/animated_container.dart",
"lib/widgets/animation_builder.dart",
"lib/widgets/basic.dart",
"lib/widgets/block_viewport.dart",
......
......@@ -4,9 +4,27 @@
import 'dart:sky';
num lerpNum(num a, num b, double t) => a + (b - a) * t;
num lerpNum(num a, num b, double t) {
if (a == null && b == null)
return null;
if (a == null)
a = 0.0;
if (b == null)
b = 0.0;
return a + (b - a) * t;
}
Color _scaleAlpha(Color a, double factor) {
return a.withAlpha((a.alpha * factor).round());
}
Color lerpColor(Color a, Color b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return _scaleAlpha(b, t);
if (b == null)
return _scaleAlpha(b, 1.0 - t);
return new Color.fromARGB(
lerpNum(a.alpha, b.alpha, t).toInt(),
lerpNum(a.red, b.red, t).toInt(),
......@@ -15,5 +33,11 @@ Color lerpColor(Color a, Color b, double t) {
}
Offset lerpOffset(Offset a, Offset b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return b * t;
if (b == null)
return a * (1.0 - t);
return new Offset(lerpNum(a.dx, b.dx, t), lerpNum(a.dy, b.dy, t));
}
......@@ -70,16 +70,48 @@ class BoxShadow {
final Offset offset;
final double blur;
BoxShadow scale(double factor) {
return new BoxShadow(
color: color,
offset: offset * factor,
blur: blur * factor
);
}
String toString() => 'BoxShadow($color, $offset, $blur)';
}
BoxShadow lerpBoxShadow(BoxShadow a, BoxShadow b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return b.scale(t);
if (b == null)
return a.scale(1.0 - t);
return new BoxShadow(
color: lerpColor(a.color, b.color, t),
offset: lerpOffset(a.offset, b.offset, t),
blur: lerpNum(a.blur, b.blur, t));
}
List<BoxShadow> lerpListBoxShadow(List<BoxShadow> a, List<BoxShadow> b, double t) {
if (a == null && b == null)
return null;
if (a == null)
a = new List<BoxShadow>();
if (b == null)
b = new List<BoxShadow>();
List<BoxShadow> result = new List<BoxShadow>();
int commonLength = math.min(a.length, b.length);
for (int i = 0; i < commonLength; ++i)
result.add(lerpBoxShadow(a[i], b[i], t));
for (int i = commonLength; i < a.length; ++i)
result.add(a[i].scale(1.0 - t));
for (int i = commonLength; i < b.length; ++i)
result.add(b[i].scale(t));
return result;
}
abstract class Gradient {
sky.Shader createShader();
}
......@@ -198,6 +230,19 @@ class BoxDecoration {
final Gradient gradient;
final Shape shape;
BoxDecoration scale(double factor) {
// TODO(abarth): Scale ALL the things.
return new BoxDecoration(
backgroundColor: lerpColor(null, backgroundColor, factor),
backgroundImage: backgroundImage,
border: border,
borderRadius: lerpNum(null, borderRadius, factor),
boxShadow: lerpListBoxShadow(null, boxShadow, factor),
gradient: gradient,
shape: shape
);
}
String toString([String prefix = '']) {
List<String> result = [];
if (backgroundColor != null)
......@@ -220,6 +265,25 @@ class BoxDecoration {
}
}
BoxDecoration lerpBoxDecoration(BoxDecoration a, BoxDecoration b, double t) {
if (a == null && b == null)
return null;
if (a == null)
return b.scale(t);
if (b == null)
return a.scale(1.0 - t);
// TODO(abarth): lerp ALL the fields.
return new BoxDecoration(
backgroundColor: lerpColor(a.backgroundColor, b.backgroundColor, t),
backgroundImage: b.backgroundImage,
border: b.border,
borderRadius: lerpNum(a.borderRadius, b.borderRadius, t),
boxShadow: lerpListBoxShadow(a.boxShadow, b.boxShadow, t),
gradient: b.gradient,
shape: b.shape
);
}
class BoxPainter {
BoxPainter(BoxDecoration decoration) : _decoration = decoration {
assert(decoration != null);
......
// 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:vector_math/vector_math.dart';
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/animation/curves.dart';
import 'package:sky/base/lerp.dart';
import 'package:sky/painting/box_painter.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/animated_component.dart';
class AnimatedBoxConstraintsValue extends AnimatedType<BoxConstraints> {
AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setFraction(double t) {
// TODO(abarth): We should lerp the BoxConstraints.
value = end;
}
}
class AnimatedBoxDecorationValue extends AnimatedType<BoxDecoration> {
AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setFraction(double t) {
if (t == 1.0) {
value = end;
return;
}
value = lerpBoxDecoration(begin, end, t);
}
}
class AnimatedEdgeDimsValue extends AnimatedType<EdgeDims> {
AnimatedEdgeDimsValue(EdgeDims begin, { EdgeDims end, Curve curve: linear })
: super(begin, end: end, curve: curve);
void setFraction(double t) {
if (t == 1.0) {
value = end;
return;
}
value = new EdgeDims(
lerpNum(begin.top, end.top, t),
lerpNum(begin.right, end.right, t),
lerpNum(begin.bottom, end.bottom, t),
lerpNum(begin.bottom, end.left, t)
);
}
}
class ImplicitlyAnimatedValue<T> {
final AnimationPerformance performance = new AnimationPerformance();
final AnimatedType<T> _variable;
ImplicitlyAnimatedValue(this._variable, Duration duration) {
performance
..variable = _variable
..duration = duration;
}
T get value => _variable.value;
void set value(T newValue) {
_variable.begin = _variable.value;
_variable.end = newValue;
if (_variable.value != _variable.end) {
performance
..progress = 0.0
..play();
}
}
}
class AnimatedContainer extends AnimatedComponent {
AnimatedContainer({
String key,
this.child,
this.duration,
this.constraints,
this.decoration,
this.width,
this.height,
this.margin,
this.padding,
this.transform
}) : super(key: key);
Widget child;
Duration duration; // TODO(abarth): Support separate durations for each value.
BoxConstraints constraints;
BoxDecoration decoration;
EdgeDims margin;
EdgeDims padding;
Matrix4 transform;
double width;
double height;
ImplicitlyAnimatedValue<BoxConstraints> _constraints;
ImplicitlyAnimatedValue<BoxDecoration> _decoration;
ImplicitlyAnimatedValue<EdgeDims> _margin;
ImplicitlyAnimatedValue<EdgeDims> _padding;
ImplicitlyAnimatedValue<Matrix4> _transform;
ImplicitlyAnimatedValue<double> _width;
ImplicitlyAnimatedValue<double> _height;
void initState() {
_updateFields();
}
void syncFields(AnimatedContainer source) {
child = source.child;
constraints = source.constraints;
decoration = source.decoration;
margin = source.margin;
padding = source.padding;
width = source.width;
height = source.height;
_updateFields();
}
void _updateFields() {
_updateConstraints();
_updateDecoration();
_updateMargin();
_updatePadding();
_updateTransform();
_updateWidth();
_updateHeight();
}
void _updateField(dynamic value, ImplicitlyAnimatedValue animatedValue, Function initField) {
if (animatedValue != null)
animatedValue.value = value;
else if (value != null)
initField();
}
void _updateConstraints() {
_updateField(constraints, _constraints, () {
_constraints = new ImplicitlyAnimatedValue<BoxConstraints>(new AnimatedBoxConstraintsValue(constraints), duration);
watch(_constraints.performance);
});
}
void _updateDecoration() {
_updateField(decoration, _decoration, () {
_decoration = new ImplicitlyAnimatedValue<BoxDecoration>(new AnimatedBoxDecorationValue(decoration), duration);
watch(_decoration.performance);
});
}
void _updateMargin() {
_updateField(margin, _margin, () {
_margin = new ImplicitlyAnimatedValue<EdgeDims>(new AnimatedEdgeDimsValue(margin), duration);
watch(_margin.performance);
});
}
void _updatePadding() {
_updateField(padding, _padding, () {
_padding = new ImplicitlyAnimatedValue<EdgeDims>(new AnimatedEdgeDimsValue(padding), duration);
watch(_padding.performance);
});
}
void _updateTransform() {
_updateField(transform, _transform, () {
_transform = new ImplicitlyAnimatedValue<Matrix4>(new AnimatedType<Matrix4>(transform), duration);
watch(_transform.performance);
});
}
void _updateWidth() {
_updateField(width, _width, () {
_width = new ImplicitlyAnimatedValue<double>(new AnimatedType<double>(width), duration);
watch(_width.performance);
});
}
void _updateHeight() {
_updateField(height, _height, () {
_height = new ImplicitlyAnimatedValue<double>( new AnimatedType<double>(height), duration);
watch(_height.performance);
});
}
dynamic _getValue(dynamic value, ImplicitlyAnimatedValue animatedValue) {
return animatedValue == null ? value : animatedValue.value;
}
Widget build() {
return new Container(
child: child,
constraints: _getValue(constraints, _constraints),
decoration: _getValue(decoration, _decoration),
margin: _getValue(margin, _margin),
padding: _getValue(padding, _padding),
transform: _getValue(transform, _transform),
width: _getValue(width, _width),
height: _getValue(height, _height)
);
}
}
......@@ -2,10 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:sky/animation/animation_performance.dart';
import 'package:sky/painting/box_painter.dart';
import 'package:sky/widgets/animated_component.dart';
import 'package:sky/widgets/animation_builder.dart';
import 'package:sky/theme/shadows.dart';
import 'package:sky/widgets/animated_container.dart';
import 'package:sky/widgets/basic.dart';
import 'package:sky/widgets/default_text_style.dart';
import 'package:sky/widgets/theme.dart';
......@@ -19,11 +18,7 @@ const Map<MaterialType, double> edges = const {
MaterialType.button: 2.0,
};
const Duration _kAnimateShadowDuration = const Duration(milliseconds: 100);
const Duration _kAnimateColorDuration = const Duration(milliseconds: 100);
class Material extends AnimatedComponent {
class Material extends Component {
Material({
String key,
this.child,
......@@ -34,64 +29,37 @@ class Material extends AnimatedComponent {
assert(level != null);
}
Widget child;
MaterialType type;
int level;
Color color;
final AnimationBuilder _builder = new AnimationBuilder();
void initState() {
_builder
..shadow = new AnimatedType<double>(level.toDouble())
..backgroundColor = _getBackgroundColor(type, color)
..borderRadius = edges[type]
..shape = type == MaterialType.circle ? Shape.circle : Shape.rectangle;
watch(_builder.createPerformance(
[_builder.shadow],
duration: _kAnimateShadowDuration
));
watch(_builder.createPerformance(
[_builder.backgroundColor],
duration: _kAnimateColorDuration
));
super.initState();
}
void syncFields(Material source) {
child = source.child;
type = source.type;
level = source.level;
color = source.color;
_builder.updateFields(
shadow: new AnimatedType<double>(level.toDouble()),
backgroundColor: _getBackgroundColor(type, color),
borderRadius: edges[type],
shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle
);
super.syncFields(source);
}
final Widget child;
final MaterialType type;
final int level;
final Color color;
AnimatedColor _getBackgroundColor(MaterialType type, Color color) {
if (color == null) {
Color get _backgroundColor {
if (color != null)
return color;
switch (type) {
case MaterialType.canvas:
color = Theme.of(this).canvasColor;
break;
return Theme.of(this).canvasColor;
case MaterialType.card:
color = Theme.of(this).cardColor;
break;
return Theme.of(this).cardColor;
default:
break;
}
return null;
}
return color == null ? null : new AnimatedColor(color);
}
Widget build() {
return _builder.build(
new DefaultTextStyle(style: Theme.of(this).text.body1, child: child)
return new AnimatedContainer(
duration: const Duration(milliseconds: 1000),
decoration: new BoxDecoration(
backgroundColor: _backgroundColor,
borderRadius: edges[type],
boxShadow: level == 0 ? null : shadows[level],
shape: type == MaterialType.circle ? Shape.circle : Shape.rectangle
),
child: new DefaultTextStyle(
style: Theme.of(this).text.body1,
child: child
)
);
}
}
......@@ -189,7 +189,7 @@ PAINT FOR FRAME #3 ----------------------------------------------
3 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
3 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
3 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
3 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
3 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
3 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
3 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
3 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......@@ -318,7 +318,7 @@ PAINT FOR FRAME #4 ----------------------------------------------
4 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
4 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
4 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
4 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
4 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
4 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
4 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
4 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......@@ -450,7 +450,7 @@ PAINT FOR FRAME #5 ----------------------------------------------
5 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
5 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
5 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
5 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
5 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
5 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
5 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
5 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......@@ -585,7 +585,7 @@ PAINT FOR FRAME #6 ----------------------------------------------
6 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
6 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
6 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
6 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
6 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
6 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
6 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
6 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......@@ -717,7 +717,7 @@ PAINT FOR FRAME #7 ----------------------------------------------
7 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
7 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
7 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
7 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
7 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
7 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
7 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
7 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......@@ -846,7 +846,7 @@ PAINT FOR FRAME #8 ----------------------------------------------
8 | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
8 | | | | | | | | | paintChild RenderDecoratedBox at Point(496.75, 89.0)
8 | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
8 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6)))
8 | | | | | | | | | | drawRRect(Instance of 'RRect', Paint(color:Color(0xffd6d6d6), drawLooper:true))
8 | | | | | | | | | | paintChild RenderInkWell at Point(496.75, 89.0)
8 | | | | | | | | | | | TestPaintingCanvas() constructor: 800.0 x 600.0
8 | | | | | | | | | | | paintChild RenderPadding at Point(496.75, 89.0)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册