animated_container.dart 7.84 KB
Newer Older
1 2 3 4
// 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.

Adam Barth's avatar
Adam Barth committed
5
import 'dart:sky' as sky;
6

Adam Barth's avatar
Adam Barth committed
7
import 'package:vector_math/vector_math.dart';
8
import 'package:sky/animation.dart';
9
import 'package:sky/painting.dart';
10 11
import 'package:sky/src/widgets/basic.dart';
import 'package:sky/src/widgets/framework.dart';
12

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

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

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

25
  BoxDecoration lerp(double t) => BoxDecoration.lerp(begin, end, t);
26 27
}

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

32 33
  EdgeDims lerp(double t) {
    return new EdgeDims(
Adam Barth's avatar
Adam Barth committed
34 35 36 37
      sky.lerpDouble(begin.top, end.top, t),
      sky.lerpDouble(begin.right, end.right, t),
      sky.lerpDouble(begin.bottom, end.bottom, t),
      sky.lerpDouble(begin.bottom, end.left, t)
38 39 40 41
    );
  }
}

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

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

56 57
abstract class AnimationBehavior {
  void initFields(AnimatedContainer original);
58
  void syncConstructorArguments(AnimatedContainer original, AnimatedContainer updated);
59 60
}

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

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

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

83 84
abstract class ImplicitlyAnimatedFieldBehavior<T> extends AnimationBehavior {
  ImplicitlyAnimatedFieldBehavior(this.duration);
85 86 87 88 89 90 91 92 93 94 95 96 97

  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));
  }

98
  void syncConstructorArguments(AnimatedContainer original, AnimatedContainer updated) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    _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); });
      });
    }
  }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

184
class AnimatedContainer extends StatefulComponent {
185
  AnimatedContainer({
186
    Key key,
187
    this.child,
188
    this.behavior,
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    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;

207
  List<AnimationBehavior> behavior;
208 209

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

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

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