animated_container.dart 7.9 KB
Newer Older
1 2 3 4 5 6
// 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';

7
import 'package:sky/animation/animated_value.dart';
8 9 10 11 12 13 14
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';

15
class AnimatedBoxConstraintsValue extends AnimatedValue<BoxConstraints> {
16 17 18
  AnimatedBoxConstraintsValue(BoxConstraints begin, { BoxConstraints end, Curve curve: linear })
    : super(begin, end: end, curve: curve);

19 20
  // TODO(abarth): We should lerp the BoxConstraints.
  BoxConstraints lerp(double t) => end;
21 22
}

23
class AnimatedBoxDecorationValue extends AnimatedValue<BoxDecoration> {
24 25 26
  AnimatedBoxDecorationValue(BoxDecoration begin, { BoxDecoration end, Curve curve: linear })
    : super(begin, end: end, curve: curve);

27
  BoxDecoration lerp(double t) => lerpBoxDecoration(begin, end, t);
28 29
}

30
class AnimatedEdgeDimsValue extends AnimatedValue<EdgeDims> {
31 32 33
  AnimatedEdgeDimsValue(EdgeDims begin, { EdgeDims end, Curve curve: linear })
    : super(begin, end: end, curve: curve);

34 35
  EdgeDims lerp(double t) {
    return new EdgeDims(
36 37 38 39 40 41 42 43
      lerpNum(begin.top, end.top, t),
      lerpNum(begin.right, end.right, t),
      lerpNum(begin.bottom, end.bottom, t),
      lerpNum(begin.bottom, end.left, t)
    );
  }
}

44 45 46 47
class AnimatedMatrix4Value extends AnimatedValue<Matrix4> {
  AnimatedMatrix4Value(Matrix4 begin, { Matrix4 end, Curve curve: linear })
    : super(begin, end: end, curve: curve);

48
  Matrix4 lerp(double t) {
49 50 51 52 53
    // 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;
54
    return new Matrix4.identity()..translate(lerpT);
55 56 57
  }
}

58 59 60 61 62
abstract class AnimationBehavior {
  void initFields(AnimatedContainer original);
  void syncFields(AnimatedContainer original, AnimatedContainer updated);
}

63 64
class ImplicitlyAnimatedValue<T> {
  final AnimationPerformance performance = new AnimationPerformance();
65
  final AnimatedValue<T> _variable;
66 67 68 69 70 71 72 73

  ImplicitlyAnimatedValue(this._variable, Duration duration) {
    performance
      ..variable = _variable
      ..duration = duration;
  }

  T get value => _variable.value;
74
  void animateTo(T newValue) {
75 76 77 78 79 80 81 82 83 84
    _variable.begin = _variable.value;
    _variable.end = newValue;
    if (_variable.value != _variable.end) {
      performance
        ..progress = 0.0
        ..play();
    }
  }
}

85 86
abstract class ImplicitlyAnimatedFieldBehavior<T> extends AnimationBehavior {
  ImplicitlyAnimatedFieldBehavior(this.duration);
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117

  Duration duration;
  ImplicitlyAnimatedValue<T> field;

  // Overrides.
  T getter(AnimatedContainer container);
  void setter(AnimatedContainer container, T value);
  AnimatedValue<T> initField(T value);

  void initFields(AnimatedContainer original) {
    _updateField(original, getter(original));
  }

  void syncFields(AnimatedContainer original, AnimatedContainer updated) {
    _updateField(original, getter(updated));
  }

  void _updateField(AnimatedContainer original, T newValue) {
    if (field != null) {
      // Animate to newValue (possibly null).
      field.animateTo(newValue);
    } else if (newValue != null) {
      // Set the value and prepare it for future animations.
      field = new ImplicitlyAnimatedValue<T>(initField(newValue), duration);
      field.performance.addListener(() {
        original.setState(() { setter(original, field.value); });
      });
    }
  }
}

118 119
class ImplicitlyAnimatedConstraintsBehavior extends ImplicitlyAnimatedFieldBehavior<BoxConstraints> {
  ImplicitlyAnimatedConstraintsBehavior(Duration duration) : super(duration);
120 121 122 123 124 125

  BoxConstraints getter(AnimatedContainer container) => container.constraints;
  void setter(AnimatedContainer container, BoxConstraints val) { container.constraints = val; }
  AnimatedValue initField(BoxConstraints val) => new AnimatedBoxConstraintsValue(val);
}

126 127
class ImplicitlyAnimatedDecorationBehavior extends ImplicitlyAnimatedFieldBehavior<BoxDecoration> {
  ImplicitlyAnimatedDecorationBehavior(Duration duration) : super(duration);
128 129 130 131 132 133

  BoxDecoration getter(AnimatedContainer container) => container.decoration;
  void setter(AnimatedContainer container, BoxDecoration val) { container.decoration = val; }
  AnimatedValue initField(BoxDecoration val) => new AnimatedBoxDecorationValue(val);
}

134 135
class ImplicitlyAnimatedMarginBehavior extends ImplicitlyAnimatedFieldBehavior<EdgeDims> {
  ImplicitlyAnimatedMarginBehavior(Duration duration) : super(duration);
136 137 138 139 140 141

  EdgeDims getter(AnimatedContainer container) => container.margin;
  void setter(AnimatedContainer container, EdgeDims val) { container.margin = val; }
  AnimatedValue initField(EdgeDims val) => new AnimatedEdgeDimsValue(val);
}

142 143
class ImplicitlyAnimatedPaddingBehavior extends ImplicitlyAnimatedFieldBehavior<EdgeDims> {
  ImplicitlyAnimatedPaddingBehavior(Duration duration) : super(duration);
144 145 146 147 148 149

  EdgeDims getter(AnimatedContainer container) => container.padding;
  void setter(AnimatedContainer container, EdgeDims val) { container.padding = val; }
  AnimatedValue initField(EdgeDims val) => new AnimatedEdgeDimsValue(val);
}

150 151
class ImplicitlyAnimatedTransformBehavior extends ImplicitlyAnimatedFieldBehavior<Matrix4> {
  ImplicitlyAnimatedTransformBehavior(Duration duration) : super(duration);
152 153 154 155 156 157

  Matrix4 getter(AnimatedContainer container) => container.transform;
  void setter(AnimatedContainer container, Matrix4 val) { container.transform = val; }
  AnimatedValue initField(Matrix4 val) => new AnimatedMatrix4Value(val);
}

158 159
class ImplicitlyAnimatedWidthBehavior extends ImplicitlyAnimatedFieldBehavior<double> {
  ImplicitlyAnimatedWidthBehavior(Duration duration) : super(duration);
160 161 162 163 164 165

  double getter(AnimatedContainer container) => container.width;
  void setter(AnimatedContainer container, double val) { container.width = val; }
  AnimatedValue initField(double val) => new AnimatedValue<double>(val);
}

166 167
class ImplicitlyAnimatedHeightBehavior extends ImplicitlyAnimatedFieldBehavior<double> {
  ImplicitlyAnimatedHeightBehavior(Duration duration) : super(duration);
168 169 170 171 172 173

  double getter(AnimatedContainer container) => container.height;
  void setter(AnimatedContainer container, double val) { container.height = val; }
  AnimatedValue initField(double val) => new AnimatedValue<double>(val);
}

174
List<AnimationBehavior> implicitlyAnimate(Duration duration) {
175
  return [
176 177 178 179 180 181 182
    new ImplicitlyAnimatedConstraintsBehavior(duration),
    new ImplicitlyAnimatedDecorationBehavior(duration),
    new ImplicitlyAnimatedMarginBehavior(duration),
    new ImplicitlyAnimatedPaddingBehavior(duration),
    new ImplicitlyAnimatedTransformBehavior(duration),
    new ImplicitlyAnimatedWidthBehavior(duration),
    new ImplicitlyAnimatedHeightBehavior(duration)
183 184 185
  ];
}

186 187
class AnimatedContainer extends AnimatedComponent {
  AnimatedContainer({
188
    Key key,
189
    this.child,
190
    this.behavior,
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
    this.constraints,
    this.decoration,
    this.width,
    this.height,
    this.margin,
    this.padding,
    this.transform
  }) : super(key: key);

  Widget child;
  BoxConstraints constraints;
  BoxDecoration decoration;
  EdgeDims margin;
  EdgeDims padding;
  Matrix4 transform;
  double width;
  double height;

209
  List<AnimationBehavior> behavior;
210 211

  void initState() {
212
    for (AnimationBehavior i in behavior)
213
      i.initFields(this);
214 215
  }

216 217
  void syncFields(AnimatedContainer updated) {
    child = updated.child;
218
    for (AnimationBehavior i in behavior)
219
      i.syncFields(this, updated);
220 221 222 223 224
  }

  Widget build() {
    return new Container(
      child: child,
225 226 227 228 229 230 231
      constraints: constraints,
      decoration: decoration,
      margin: margin,
      padding: padding,
      transform: transform,
      width: width,
      height: height
232 233 234
    );
  }
}