tween.dart 7.22 KB
Newer Older
1 2 3 4
// Copyright 2016 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.

5
import 'dart:ui' show Color, Size, Rect;
6

7 8
import 'animation.dart';
import 'animations.dart';
9
import 'curves.dart';
10

11
/// An object that can produce a value of type T given an [Animation] as input.
12
abstract class Animatable<T> {
13 14
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
15
  const Animatable();
16

17
  /// The current value of this object for the given animation.
18
  T evaluate(Animation<double> animation);
19

20 21
  /// Returns a new Animation that is driven by the given animation but that
  /// takes on values determined by this object.
22
  Animation<T> animate(Animation<double> parent) {
23
    return new _AnimatedEvaluation<T>(parent, this);
24
  }
25

26 27
  /// Returns a new Animatable whose value is determined by first evaluating
  /// the given parent and then evaluating this object.
28
  Animatable<T> chain(Animatable<double> parent) {
29 30
    return new _ChainedEvaluation<T>(parent, this);
  }
31 32
}

33
class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
34
  _AnimatedEvaluation(this.parent, this._evaluatable);
35

36
  @override
37
  final Animation<double> parent;
38

39
  final Animatable<T> _evaluatable;
40

41
  @override
42
  T get value => _evaluatable.evaluate(parent);
43 44 45

  @override
  String toString() {
46
    return '$parent\u27A9$_evaluatable\u27A9$value';
47 48 49 50 51 52
  }

  @override
  String toStringDetails() {
    return '${super.toStringDetails()} $_evaluatable';
  }
53 54
}

55
class _ChainedEvaluation<T> extends Animatable<T> {
56 57
  _ChainedEvaluation(this._parent, this._evaluatable);

58 59
  final Animatable<double> _parent;
  final Animatable<T> _evaluatable;
60

61
  @override
62
  T evaluate(Animation<double> animation) {
63
    double value = _parent.evaluate(animation);
64
    return _evaluatable.evaluate(new AlwaysStoppedAnimation<double>(value));
65
  }
66 67 68 69 70

  @override
  String toString() {
    return '$_parent\u27A9$_evaluatable';
  }
71 72
}

73
/// A linear interpolation between a beginning and ending value.
74 75 76 77 78 79 80 81 82 83
///
/// [Tween] is useful if you want to interpolate across a range.
///
/// To use a [Tween] object with an animation, call the [Tween] object's `animate()` method and
/// pass it the [Animation] object that you want to modify.
///
/// You can chain [Tween] objects together using the `chain()` method, so that a single
/// [Animation] object is configured by multiple [Tween] objects called in succession. This is
/// different than calling the `animate()` method twice, which results in two [Animation]
/// separate objects, each configured with a single [Tween].
84
class Tween<T extends dynamic> extends Animatable<T> {
85 86 87
  /// Creates a tween.
  ///
  /// The [begin] and [end] arguments must not be null.
88
  Tween({ this.begin, this.end });
89 90 91 92 93 94 95 96 97 98

  /// The value this variable has at the beginning of the animation.
  T begin;

  /// The value this variable has at the end of the animation.
  T end;

  /// Returns the value this variable has at the given animation clock value.
  T lerp(double t) => begin + (end - begin) * t;

99
  /// Returns the interpolated value for the current value of the given animation.
100 101
  ///
  /// This method returns `begin` and `end` when the animation values are 0.0 or 1.0, respectively.
102
  @override
103
  T evaluate(Animation<double> animation) {
104
    final double t = animation.value;
105 106 107 108 109 110
    if (t == 0.0)
      return begin;
    if (t == 1.0)
      return end;
    return lerp(t);
  }
111

112
  @override
113
  String toString() => '$runtimeType($begin \u2192 $end)';
114 115
}

116
/// An interpolation between two colors.
117 118 119 120
///
/// This class specializes the interpolation of Tween<Color> to be
/// appropriate for colors.
class ColorTween extends Tween<Color> {
121 122 123
  /// Creates a color tween.
  ///
  /// The [begin] and [end] arguments must not be null.
124 125
  ColorTween({ Color begin, Color end }) : super(begin: begin, end: end);

126
  @override
127 128 129
  Color lerp(double t) => Color.lerp(begin, end, t);
}

130
/// An interpolation between two sizes.
131 132 133 134
///
/// This class specializes the interpolation of Tween<Size> to be
/// appropriate for rectangles.
class SizeTween extends Tween<Size> {
135 136 137
  /// Creates a size tween.
  ///
  /// The [begin] and [end] arguments must not be null.
138 139
  SizeTween({ Size begin, Size end }) : super(begin: begin, end: end);

140
  @override
141 142 143
  Size lerp(double t) => Size.lerp(begin, end, t);
}

144
/// An interpolation between two rectangles.
145 146 147 148
///
/// This class specializes the interpolation of Tween<Rect> to be
/// appropriate for rectangles.
class RectTween extends Tween<Rect> {
149 150 151
  /// Creates a rect tween.
  ///
  /// The [begin] and [end] arguments must not be null.
152 153
  RectTween({ Rect begin, Rect end }) : super(begin: begin, end: end);

154
  @override
155 156 157
  Rect lerp(double t) => Rect.lerp(begin, end, t);
}

158
/// An interpolation between two integers that rounds.
159 160
///
/// This class specializes the interpolation of Tween<int> to be
161 162 163 164 165 166
/// appropriate for integers by interpolating between the given begin
/// and end values and then rounding the result to the nearest
/// integer.
///
/// This is the closest approximation to a linear tween that is
/// possible with an integer. Compare to [StepTween].
167
class IntTween extends Tween<int> {
168 169 170
  /// Creates an int tween.
  ///
  /// The [begin] and [end] arguments must not be null.
171 172 173 174
  IntTween({ int begin, int end }) : super(begin: begin, end: end);

  // The inherited lerp() function doesn't work with ints because it multiplies
  // the begin and end types by a double, and int * double returns a double.
175
  @override
176 177
  int lerp(double t) => (begin + (end - begin) * t).round();
}
178

179 180 181 182 183 184 185 186 187 188
/// An interpolation between two integers that floors.
///
/// This class specializes the interpolation of Tween<int> to be
/// appropriate for integers by interpolating between the given begin
/// and end values and then using [int.floor()] to return the current
/// integer component, dropping the fractional component.
///
/// This results in a value that is never greater than the equivalent
/// value from a linear double interpolation. Compare to [IntTween].
class StepTween extends Tween<int> {
189 190 191
  /// Creates a step tween.
  ///
  /// The [begin] and [end] arguments must not be null.
192 193 194 195
  StepTween({ int begin, int end }) : super(begin: begin, end: end);

  // The inherited lerp() function doesn't work with ints because it multiplies
  // the begin and end types by a double, and int * double returns a double.
196
  @override
197 198 199
  int lerp(double t) => (begin + (end - begin) * t).floor();
}

200 201 202 203 204
/// Transforms the value of the given animation by the given curve.
///
/// This class differs from [CurvedAnimation] in that [CurvedAnimation] applies
/// a curve to an existing [Animation] object whereas [CurveTween] can be
/// chained with another [Tween] prior to receiving the underlying [Animation].
205
class CurveTween extends Animatable<double> {
206 207 208 209 210 211
  /// Creates a curve tween.
  ///
  /// The [curve] argument must not be null.
  CurveTween({ this.curve }) {
    assert(curve != null);
  }
212

213
  /// The curve to use when transforming the value of the animation.
214 215
  Curve curve;

216
  @override
217
  double evaluate(Animation<double> animation) {
218 219 220 221 222 223 224 225
    double t = animation.value;
    if (t == 0.0 || t == 1.0) {
      assert(curve.transform(t).round() == t);
      return t;
    }
    return curve.transform(t);
  }
}