Chromium Code Reviews| Index: sky/sdk/lib/widgets/animated_container.dart |
| diff --git a/sky/sdk/lib/widgets/animated_container.dart b/sky/sdk/lib/widgets/animated_container.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9248785e44ec7f6a9554cba2c282cb977cc0e008 |
| --- /dev/null |
| +++ b/sky/sdk/lib/widgets/animated_container.dart |
| @@ -0,0 +1,140 @@ |
| +// 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 'dart:async'; |
| + |
| +import 'package:vector_math/vector_math.dart'; |
| + |
| +import '../animation/animation_performance.dart'; |
| +import '../animation/curves.dart'; |
| +import '../base/lerp.dart'; |
| +import '../painting/box_painter.dart'; |
| +import '../theme/shadows.dart'; |
| +import 'animated_component.dart'; |
| +import 'basic.dart'; |
| + |
| +// Types of things that can be animated in a component. Use build() to |
| +// construct the final Widget based on the animation state. |
| +// TODO(mpcomplete): the idea here is to eventually have an AnimatedCollection |
| +// which assembles a container based on a list of animated things. e.g. if you |
| +// want to animate position, opacity, and shadow, you add those animators to an |
| +// AnimatedCollection and just call collection.build() to construct your |
| +// widget. |
| + |
| +List<BoxShadow> _computeShadow(double level) { |
| + if (level < 1.0) // shadows[1] is the first shadow |
| + return null; |
| + |
| + int level1 = level.floor(); |
| + int level2 = level.ceil(); |
| + double t = level - level1.toDouble(); |
| + |
| + List<BoxShadow> shadow = new List<BoxShadow>(); |
| + for (int i = 0; i < shadows[level1].length; ++i) |
| + shadow.add(lerpBoxShadow(shadows[level1][i], shadows[level2][i], t)); |
| + return shadow; |
| +} |
| + |
| +class AnimatedColor extends AnimatedType<Color> { |
| + AnimatedColor(Color begin, {Color end, Curve curve: linear}) |
| + : super(begin, end: end, curve: curve); |
| + |
| + void setFraction(double t) { |
| + value = lerpColor(begin, end, t); |
| + } |
| +} |
| + |
| +enum AnimatedContainerSlots { |
| + position, |
| + shadow, |
| + backgroundColor |
| +} |
|
abarth-chromium
2015/07/08 21:02:05
This seems overly general. Why not just use expli
Matt Perry
2015/07/08 21:41:03
Done.
|
| + |
| +class _Entry { |
| + AnimatedType variable; |
| + AnimationPerformance performance; |
| +} |
| + |
| +class AnimatedContainer { |
| + AnimatedType<Point> get position => _getSlot(AnimatedContainerSlots.position); |
| + void set position(AnimatedType<Point> variable) { |
| + _setSlot(AnimatedContainerSlots.position, variable); |
| + } |
| + AnimatedType<double> get shadow => _getSlot(AnimatedContainerSlots.shadow); |
| + void set shadow(AnimatedType<double> variable) { |
| + _setSlot(AnimatedContainerSlots.shadow, variable); |
| + } |
| + AnimatedColor get backgroundColor => _getSlot(AnimatedContainerSlots.backgroundColor); |
| + void set backgroundColor(AnimatedColor variable) { |
| + _setSlot(AnimatedContainerSlots.backgroundColor, variable); |
| + } |
| + |
| + AnimatedType _getSlot(AnimatedContainerSlots slot) { |
| + if (_slots[slot] == null) |
| + return null; |
| + return _slots[slot].variable; |
| + } |
| + void _setSlot(AnimatedContainerSlots slot, AnimatedType variable) { |
| + _slots[slot] = new _Entry()..variable = variable; |
| + } |
| + |
| + Map<AnimatedContainerSlots, _Entry> _slots = |
| + new Map<AnimatedContainerSlots, _Entry>(); |
| + List<AnimationPerformance> _performances = []; |
| + |
| + // TODO(mpcomplete): don't rely on AnimatedComponent. |
| + AnimatedComponent _component; |
| + AnimatedContainer([this._component]); |
| + |
| + AnimationPerformance createPerformance(List<AnimatedContainerSlots> slots, |
| + {Duration duration}) { |
| + List<AnimatedVariable> variables = slots.map((e) => _slots[e].variable).toList(); |
| + AnimationPerformance performance = new AnimationPerformance() |
| + ..duration = duration |
| + ..variable = new AnimatedList(variables); |
| + _performances.add(performance); |
| + for (AnimatedContainerSlots slot in slots) |
| + _slots[slot].performance = performance; |
| + if (_component != null) |
| + _component.watch(performance.timeline); |
| + return performance; |
| + } |
| + |
| + Widget build(Widget child) { |
| + Widget current = child; |
| + if (shadow != null || backgroundColor != null) { |
| + current = new DecoratedBox( |
| + decoration: new BoxDecoration( |
| + boxShadow: shadow != null ? _computeShadow(shadow.value) : null, |
| + backgroundColor: backgroundColor != null ? backgroundColor.value : null), |
| + child: current); |
| + } |
| + |
| + if (position != null) { |
| + Matrix4 transform = new Matrix4.identity(); |
| + transform.translate(position.value.x, position.value.y); |
| + current = new Transform(transform: transform, child: child); |
| + } |
| + |
| + return current; |
| + } |
| + |
| + bool syncFields(AnimatedContainer source) { |
| + for (AnimatedContainerSlots slot in AnimatedContainerSlots.values) { |
| + if (_slots[slot] == null) |
| + continue; // TODO(mpcomplete): Should we handle transition from null? |
| + if (_slots[slot].performance == null) { |
| + // If there's no performance, no need to animate. |
| + _slots[slot].variable = source._slots[slot].variable; |
| + } else if (_slots[slot].variable.value != source._slots[slot].variable.value) { |
| + _slots[slot].variable |
| + ..begin = _slots[slot].variable.value |
| + ..end = source._slots[slot].variable.value; |
| + _slots[slot].performance |
| + ..progress = 0.0 |
| + ..play(); |
| + } |
| + } |
| + } |
| +} |