Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d909155

Browse files
committed
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. [email protected]
1 parent 18022ca commit d909155

File tree

7 files changed

+347
-68
lines changed

7 files changed

+347
-68
lines changed

sky/engine/core/painting/Color.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ class Color {
2222

2323
bool operator ==(other) => other is Color && _value == other._value;
2424

25+
Color withAlpha(int a) {
26+
return new Color.fromARGB(a, red, green, blue);
27+
}
28+
29+
Color withRed(int r) {
30+
return new Color.fromARGB(alpha, r, green, blue);
31+
}
32+
33+
Color withGreen(int g) {
34+
return new Color.fromARGB(alpha, red, g, blue);
35+
}
36+
37+
Color withBlue(int b) {
38+
return new Color.fromARGB(alpha, red, green, b);
39+
}
40+
2541
int get hashCode => _value.hashCode;
2642
String toString() => "Color(0x${_value.toRadixString(16).padLeft(8, '0')})";
2743
}

sky/sdk/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ dart_pkg("sky") {
5050
"lib/theme/typography.dart",
5151
"lib/theme/view_configuration.dart",
5252
"lib/widgets/animated_component.dart",
53+
"lib/widgets/animated_container.dart",
5354
"lib/widgets/animation_builder.dart",
5455
"lib/widgets/basic.dart",
5556
"lib/widgets/block_viewport.dart",

sky/sdk/lib/base/lerp.dart

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,27 @@
44

55
import 'dart:sky';
66

7-
num lerpNum(num a, num b, double t) => a + (b - a) * t;
7+
num lerpNum(num a, num b, double t) {
8+
if (a == null && b == null)
9+
return null;
10+
if (a == null)
11+
a = 0.0;
12+
if (b == null)
13+
b = 0.0;
14+
return a + (b - a) * t;
15+
}
16+
17+
Color _scaleAlpha(Color a, double factor) {
18+
return a.withAlpha((a.alpha * factor).round());
19+
}
820

921
Color lerpColor(Color a, Color b, double t) {
22+
if (a == null && b == null)
23+
return null;
24+
if (a == null)
25+
return _scaleAlpha(b, t);
26+
if (b == null)
27+
return _scaleAlpha(b, 1.0 - t);
1028
return new Color.fromARGB(
1129
lerpNum(a.alpha, b.alpha, t).toInt(),
1230
lerpNum(a.red, b.red, t).toInt(),
@@ -15,5 +33,11 @@ Color lerpColor(Color a, Color b, double t) {
1533
}
1634

1735
Offset lerpOffset(Offset a, Offset b, double t) {
36+
if (a == null && b == null)
37+
return null;
38+
if (a == null)
39+
return b * t;
40+
if (b == null)
41+
return a * (1.0 - t);
1842
return new Offset(lerpNum(a.dx, b.dx, t), lerpNum(a.dy, b.dy, t));
1943
}

sky/sdk/lib/painting/box_painter.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,48 @@ class BoxShadow {
7070
final Offset offset;
7171
final double blur;
7272

73+
BoxShadow scale(double factor) {
74+
return new BoxShadow(
75+
color: color,
76+
offset: offset * factor,
77+
blur: blur * factor
78+
);
79+
}
80+
7381
String toString() => 'BoxShadow($color, $offset, $blur)';
7482
}
7583

7684
BoxShadow lerpBoxShadow(BoxShadow a, BoxShadow b, double t) {
85+
if (a == null && b == null)
86+
return null;
87+
if (a == null)
88+
return b.scale(t);
89+
if (b == null)
90+
return a.scale(1.0 - t);
7791
return new BoxShadow(
7892
color: lerpColor(a.color, b.color, t),
7993
offset: lerpOffset(a.offset, b.offset, t),
8094
blur: lerpNum(a.blur, b.blur, t));
8195
}
8296

97+
List<BoxShadow> lerpListBoxShadow(List<BoxShadow> a, List<BoxShadow> b, double t) {
98+
if (a == null && b == null)
99+
return null;
100+
if (a == null)
101+
a = new List<BoxShadow>();
102+
if (b == null)
103+
b = new List<BoxShadow>();
104+
List<BoxShadow> result = new List<BoxShadow>();
105+
int commonLength = math.min(a.length, b.length);
106+
for (int i = 0; i < commonLength; ++i)
107+
result.add(lerpBoxShadow(a[i], b[i], t));
108+
for (int i = commonLength; i < a.length; ++i)
109+
result.add(a[i].scale(1.0 - t));
110+
for (int i = commonLength; i < b.length; ++i)
111+
result.add(b[i].scale(t));
112+
return result;
113+
}
114+
83115
abstract class Gradient {
84116
sky.Shader createShader();
85117
}
@@ -198,6 +230,19 @@ class BoxDecoration {
198230
final Gradient gradient;
199231
final Shape shape;
200232

233+
BoxDecoration scale(double factor) {
234+
// TODO(abarth): Scale ALL the things.
235+
return new BoxDecoration(
236+
backgroundColor: lerpColor(null, backgroundColor, factor),
237+
backgroundImage: backgroundImage,
238+
border: border,
239+
borderRadius: lerpNum(null, borderRadius, factor),
240+
boxShadow: lerpListBoxShadow(null, boxShadow, factor),
241+
gradient: gradient,
242+
shape: shape
243+
);
244+
}
245+
201246
String toString([String prefix = '']) {
202247
List<String> result = [];
203248
if (backgroundColor != null)
@@ -220,6 +265,25 @@ class BoxDecoration {
220265
}
221266
}
222267

268+
BoxDecoration lerpBoxDecoration(BoxDecoration a, BoxDecoration b, double t) {
269+
if (a == null && b == null)
270+
return null;
271+
if (a == null)
272+
return b.scale(t);
273+
if (b == null)
274+
return a.scale(1.0 - t);
275+
// TODO(abarth): lerp ALL the fields.
276+
return new BoxDecoration(
277+
backgroundColor: lerpColor(a.backgroundColor, b.backgroundColor, t),
278+
backgroundImage: b.backgroundImage,
279+
border: b.border,
280+
borderRadius: lerpNum(a.borderRadius, b.borderRadius, t),
281+
boxShadow: lerpListBoxShadow(a.boxShadow, b.boxShadow, t),
282+
gradient: b.gradient,
283+
shape: b.shape
284+
);
285+
}
286+
223287
class BoxPainter {
224288
BoxPainter(BoxDecoration decoration) : _decoration = decoration {
225289
assert(decoration != null);
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Copyright 2015 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:vector_math/vector_math.dart';
6+
7+
import 'package:sky/animation/animation_performance.dart';
8+
import 'package:sky/animation/curves.dart';
9+
import 'package:sky/base/lerp.dart';
10+
import 'package:sky/painting/box_painter.dart';
11+
import 'package:sky/widgets/basic.dart';
12+
import 'package:sky/widgets/animated_component.dart';
13+
14+
class AnimatedBoxConstraintsValue extends AnimatedType<BoxConstraints> {
15+
AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve: linear })
16+
: super(begin, end: end, curve: curve);
17+
18+
void setFraction(double t) {
19+
// TODO(abarth): We should lerp the BoxConstraints.
20+
value = end;
21+
}
22+
}
23+
24+
class AnimatedBoxDecorationValue extends AnimatedType<BoxDecoration> {
25+
AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve: linear })
26+
: super(begin, end: end, curve: curve);
27+
28+
void setFraction(double t) {
29+
if (t == 1.0) {
30+
value = end;
31+
return;
32+
}
33+
value = lerpBoxDecoration(begin, end, t);
34+
}
35+
}
36+
37+
class AnimatedEdgeDimsValue extends AnimatedType<EdgeDims> {
38+
AnimatedEdgeDimsValue(EdgeDims begin, { EdgeDims end, Curve curve: linear })
39+
: super(begin, end: end, curve: curve);
40+
41+
void setFraction(double t) {
42+
if (t == 1.0) {
43+
value = end;
44+
return;
45+
}
46+
value = new EdgeDims(
47+
lerpNum(begin.top, end.top, t),
48+
lerpNum(begin.right, end.right, t),
49+
lerpNum(begin.bottom, end.bottom, t),
50+
lerpNum(begin.bottom, end.left, t)
51+
);
52+
}
53+
}
54+
55+
class ImplicitlyAnimatedValue<T> {
56+
final AnimationPerformance performance = new AnimationPerformance();
57+
final AnimatedType<T> _variable;
58+
59+
ImplicitlyAnimatedValue(this._variable, Duration duration) {
60+
performance
61+
..variable = _variable
62+
..duration = duration;
63+
}
64+
65+
T get value => _variable.value;
66+
void set value(T newValue) {
67+
_variable.begin = _variable.value;
68+
_variable.end = newValue;
69+
if (_variable.value != _variable.end) {
70+
performance
71+
..progress = 0.0
72+
..play();
73+
}
74+
}
75+
}
76+
77+
class AnimatedContainer extends AnimatedComponent {
78+
AnimatedContainer({
79+
String key,
80+
this.child,
81+
this.duration,
82+
this.constraints,
83+
this.decoration,
84+
this.width,
85+
this.height,
86+
this.margin,
87+
this.padding,
88+
this.transform
89+
}) : super(key: key);
90+
91+
Widget child;
92+
Duration duration; // TODO(abarth): Support separate durations for each value.
93+
BoxConstraints constraints;
94+
BoxDecoration decoration;
95+
EdgeDims margin;
96+
EdgeDims padding;
97+
Matrix4 transform;
98+
double width;
99+
double height;
100+
101+
ImplicitlyAnimatedValue<BoxConstraints> _constraints;
102+
ImplicitlyAnimatedValue<BoxDecoration> _decoration;
103+
ImplicitlyAnimatedValue<EdgeDims> _margin;
104+
ImplicitlyAnimatedValue<EdgeDims> _padding;
105+
ImplicitlyAnimatedValue<Matrix4> _transform;
106+
ImplicitlyAnimatedValue<double> _width;
107+
ImplicitlyAnimatedValue<double> _height;
108+
109+
void initState() {
110+
_updateFields();
111+
}
112+
113+
void syncFields(AnimatedContainer source) {
114+
child = source.child;
115+
constraints = source.constraints;
116+
decoration = source.decoration;
117+
margin = source.margin;
118+
padding = source.padding;
119+
width = source.width;
120+
height = source.height;
121+
_updateFields();
122+
}
123+
124+
void _updateFields() {
125+
_updateConstraints();
126+
_updateDecoration();
127+
_updateMargin();
128+
_updatePadding();
129+
_updateTransform();
130+
_updateWidth();
131+
_updateHeight();
132+
}
133+
134+
void _updateField(dynamic value, ImplicitlyAnimatedValue animatedValue, Function initField) {
135+
if (animatedValue != null)
136+
animatedValue.value = value;
137+
else if (value != null)
138+
initField();
139+
}
140+
141+
void _updateConstraints() {
142+
_updateField(constraints, _constraints, () {
143+
_constraints = new ImplicitlyAnimatedValue<BoxConstraints>(new AnimatedBoxConstraintsValue(constraints), duration);
144+
watch(_constraints.performance);
145+
});
146+
}
147+
148+
void _updateDecoration() {
149+
_updateField(decoration, _decoration, () {
150+
_decoration = new ImplicitlyAnimatedValue<BoxDecoration>(new AnimatedBoxDecorationValue(decoration), duration);
151+
watch(_decoration.performance);
152+
});
153+
}
154+
155+
void _updateMargin() {
156+
_updateField(margin, _margin, () {
157+
_margin = new ImplicitlyAnimatedValue<EdgeDims>(new AnimatedEdgeDimsValue(margin), duration);
158+
watch(_margin.performance);
159+
});
160+
}
161+
162+
void _updatePadding() {
163+
_updateField(padding, _padding, () {
164+
_padding = new ImplicitlyAnimatedValue<EdgeDims>(new AnimatedEdgeDimsValue(padding), duration);
165+
watch(_padding.performance);
166+
});
167+
}
168+
169+
void _updateTransform() {
170+
_updateField(transform, _transform, () {
171+
_transform = new ImplicitlyAnimatedValue<Matrix4>(new AnimatedType<Matrix4>(transform), duration);
172+
watch(_transform.performance);
173+
});
174+
}
175+
176+
void _updateWidth() {
177+
_updateField(width, _width, () {
178+
_width = new ImplicitlyAnimatedValue<double>(new AnimatedType<double>(width), duration);
179+
watch(_width.performance);
180+
});
181+
}
182+
183+
void _updateHeight() {
184+
_updateField(height, _height, () {
185+
_height = new ImplicitlyAnimatedValue<double>( new AnimatedType<double>(height), duration);
186+
watch(_height.performance);
187+
});
188+
}
189+
190+
dynamic _getValue(dynamic value, ImplicitlyAnimatedValue animatedValue) {
191+
return animatedValue == null ? value : animatedValue.value;
192+
}
193+
194+
Widget build() {
195+
return new Container(
196+
child: child,
197+
constraints: _getValue(constraints, _constraints),
198+
decoration: _getValue(decoration, _decoration),
199+
margin: _getValue(margin, _margin),
200+
padding: _getValue(padding, _padding),
201+
transform: _getValue(transform, _transform),
202+
width: _getValue(width, _width),
203+
height: _getValue(height, _height)
204+
);
205+
}
206+
}

0 commit comments

Comments
 (0)