tween.dart 11.5 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, hashValues;
6

7 8
import 'package:flutter/foundation.dart';

9 10
import 'animation.dart';
import 'animations.dart';
11
import 'curves.dart';
12

13 14 15 16 17
/// An object that can produce a value of type `T` given an [Animation<double>]
/// as input.
///
/// Typically, the values of the input animation are nominally in the range 0.0
/// to 1.0. In principle, however, any value could be provided.
18
abstract class Animatable<T> {
19 20
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
21
  const Animatable();
22

23
  /// The current value of this object for the given animation.
24
  T evaluate(Animation<double> animation);
25

26 27
  /// Returns a new Animation that is driven by the given animation but that
  /// takes on values determined by this object.
28
  Animation<T> animate(Animation<double> parent) {
29
    return new _AnimatedEvaluation<T>(parent, this);
30
  }
31

32 33
  /// Returns a new Animatable whose value is determined by first evaluating
  /// the given parent and then evaluating this object.
34
  Animatable<T> chain(Animatable<double> parent) {
35 36
    return new _ChainedEvaluation<T>(parent, this);
  }
37 38
}

39
class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
40
  _AnimatedEvaluation(this.parent, this._evaluatable);
41

42
  @override
43
  final Animation<double> parent;
44

45
  final Animatable<T> _evaluatable;
46

47
  @override
48
  T get value => _evaluatable.evaluate(parent);
49 50 51

  @override
  String toString() {
52
    return '$parent\u27A9$_evaluatable\u27A9$value';
53 54 55 56 57 58
  }

  @override
  String toStringDetails() {
    return '${super.toStringDetails()} $_evaluatable';
  }
59 60
}

61
class _ChainedEvaluation<T> extends Animatable<T> {
62 63
  _ChainedEvaluation(this._parent, this._evaluatable);

64 65
  final Animatable<double> _parent;
  final Animatable<T> _evaluatable;
66

67
  @override
68
  T evaluate(Animation<double> animation) {
69
    final double value = _parent.evaluate(animation);
70
    return _evaluatable.evaluate(new AlwaysStoppedAnimation<double>(value));
71
  }
72 73 74 75 76

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

79
/// A linear interpolation between a beginning and ending value.
80 81 82
///
/// [Tween] is useful if you want to interpolate across a range.
///
83
/// To use a [Tween] object with an animation, call the [Tween] object's
84
/// [animate] method and pass it the [Animation] object that you want to
85
/// modify.
86
///
87
/// You can chain [Tween] objects together using the [chain] method, so that a
88
/// single [Animation] object is configured by multiple [Tween] objects called
89
/// in succession. This is different than calling the [animate] method twice,
90 91
/// which results in two [Animation] separate objects, each configured with a
/// single [Tween].
92
///
93
/// ## Sample code
94 95 96 97 98 99
///
/// Suppose `_controller` is an [AnimationController], and we want to create an
/// [Animation<Offset>] that is controlled by that controller, and save it in
/// `_animation`:
///
/// ```dart
100
/// Animation<Offset> _animation = new Tween<Offset>(
101 102
///   begin: const Offset(100.0, 50.0),
///   end: const Offset(200.0, 300.0),
103
/// ).animate(_controller);
104 105 106 107 108 109 110
/// ```
///
/// That would provide an `_animation` that, over the lifetime of the
/// `_controller`'s animation, returns a value that depicts a point along the
/// line between the two offsets above. If we used a [MaterialPointArcTween]
/// instead of a [Tween<Offset>] in the code above, the points would follow a
/// pleasing curve instead of a straight line, with no other changes necessary.
111
class Tween<T extends dynamic> extends Animatable<T> {
112 113
  /// Creates a tween.
  ///
114 115 116
  /// The [begin] and [end] properties must be non-null before the tween is
  /// first used, but the arguments can be null if the values are going to be
  /// filled in later.
117
  Tween({ this.begin, this.end });
118 119

  /// The value this variable has at the beginning of the animation.
120
  ///
121 122
  /// See the constructor for details about whether this property may be null
  /// (it varies from subclass to subclass).
123 124 125
  T begin;

  /// The value this variable has at the end of the animation.
126
  ///
127 128
  /// See the constructor for details about whether this property may be null
  /// (it varies from subclass to subclass).
129 130 131
  T end;

  /// Returns the value this variable has at the given animation clock value.
132
  ///
133 134 135 136 137 138 139 140
  /// The default implementation of this method uses the [+], [-], and [*]
  /// operators on `T`. The [begin] and [end] properties must therefore be
  /// non-null by the time this method is called.
  T lerp(double t) {
    assert(begin != null);
    assert(end != null);
    return begin + (end - begin) * t;
  }
141

142
  /// Returns the interpolated value for the current value of the given animation.
143
  ///
144 145 146
  /// This method returns `begin` and `end` when the animation values are 0.0 or
  /// 1.0, respectively.
  ///
147 148 149 150 151 152
  /// This function is implemented by deferring to [lerp]. Subclasses that want to
  /// provide custom behavior should override [lerp], not [evaluate].
  ///
  /// See the constructor for details about whether the [begin] and [end]
  /// properties may be null when this is called. It varies from subclass to
  /// subclass.
153
  @override
154
  T evaluate(Animation<double> animation) {
155
    final double t = animation.value;
156 157 158 159 160 161
    if (t == 0.0)
      return begin;
    if (t == 1.0)
      return end;
    return lerp(t);
  }
162

163 164 165 166 167 168 169 170 171 172 173 174 175 176
  @override
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
    if (other.runtimeType != runtimeType)
      return false;
    final Tween<T> typedOther = other;
    return begin == typedOther.begin
        && end == typedOther.end;
  }

  @override
  int get hashCode => hashValues(begin, end);

177
  @override
178
  String toString() => '$runtimeType($begin \u2192 $end)';
179 180
}

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
/// A [Tween] that evaluates its [parent] in reverse.
class ReverseTween<T> extends Tween<T> {
  /// Construct a [Tween] that evaluates its [parent] in reverse.
  ReverseTween(this.parent) : assert(parent != null), super(begin: parent.end, end: parent.begin);

  /// This tween's value is the same as the parent's value evaluated in reverse.
  ///
  /// This tween's [begin] is the parent's [end] and its [end] is the parent's
  /// [begin]. The [lerp] method returns `parent.lerp(1.0 - t)` and its
  /// [evaluate] method is similar.
  final Tween<T> parent;

  @override
  T lerp(double t) => parent.lerp(1.0 - t);
}

197
/// An interpolation between two colors.
198
///
199 200 201 202
/// This class specializes the interpolation of [Tween<Color>] to use
/// [Color.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
203
class ColorTween extends Tween<Color> {
204
  /// Creates a [Color] tween.
205
  ///
206
  /// The [begin] and [end] properties may be null; the null value
207 208 209 210 211 212
  /// is treated as transparent.
  ///
  /// We recommend that you do not pass [Colors.transparent] as [begin]
  /// or [end] if you want the effect of fading in or out of transparent.
  /// Instead prefer null. [Colors.transparent] refers to black transparent and
  /// thus will fade out of or into black which is likely unwanted.
213 214
  ColorTween({ Color begin, Color end }) : super(begin: begin, end: end);

215
  /// Returns the value this variable has at the given animation clock value.
216
  @override
217 218 219
  Color lerp(double t) => Color.lerp(begin, end, t);
}

220
/// An interpolation between two sizes.
221
///
222 223 224 225
/// This class specializes the interpolation of [Tween<Size>] to use
/// [Size.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
226
class SizeTween extends Tween<Size> {
227
  /// Creates a [Size] tween.
228
  ///
229 230
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as an empty size.
231 232
  SizeTween({ Size begin, Size end }) : super(begin: begin, end: end);

233
  /// Returns the value this variable has at the given animation clock value.
234
  @override
235 236 237
  Size lerp(double t) => Size.lerp(begin, end, t);
}

238
/// An interpolation between two rectangles.
239
///
240 241 242 243
/// This class specializes the interpolation of [Tween<Rect>] to use
/// [Rect.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
244
class RectTween extends Tween<Rect> {
245
  /// Creates a [Rect] tween.
246
  ///
247 248
  /// The [begin] and [end] properties may be null; the null value
  /// is treated as an empty rect at the top left corner.
249 250
  RectTween({ Rect begin, Rect end }) : super(begin: begin, end: end);

251
  /// Returns the value this variable has at the given animation clock value.
252
  @override
253 254 255
  Rect lerp(double t) => Rect.lerp(begin, end, t);
}

256
/// An interpolation between two integers that rounds.
257
///
258
/// This class specializes the interpolation of [Tween<int>] to be
259 260 261 262
/// appropriate for integers by interpolating between the given begin
/// and end values and then rounding the result to the nearest
/// integer.
///
263 264 265 266
/// This is the closest approximation to a linear tween that is possible with an
/// integer. Compare to [StepTween] and [Tween<double>].
///
/// See [Tween] for a discussion on how to use interpolation objects.
267
class IntTween extends Tween<int> {
268 269
  /// Creates an int tween.
  ///
270 271 272
  /// The [begin] and [end] properties must be non-null before the tween is
  /// first used, but the arguments can be null if the values are going to be
  /// filled in later.
273 274 275 276
  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.
277
  @override
278 279
  int lerp(double t) => (begin + (end - begin) * t).round();
}
280

281 282
/// An interpolation between two integers that floors.
///
283
/// This class specializes the interpolation of [Tween<int>] to be
284
/// appropriate for integers by interpolating between the given begin
285
/// and end values and then using [int.floor] to return the current
286 287 288 289
/// 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].
290 291
///
/// See [Tween] for a discussion on how to use interpolation objects.
292
class StepTween extends Tween<int> {
293
  /// Creates an [int] tween that floors.
294
  ///
295 296 297
  /// The [begin] and [end] properties must be non-null before the tween is
  /// first used, but the arguments can be null if the values are going to be
  /// filled in later.
298 299 300 301
  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.
302
  @override
303 304 305
  int lerp(double t) => (begin + (end - begin) * t).floor();
}

306 307 308 309 310
/// 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].
311
class CurveTween extends Animatable<double> {
312 313 314
  /// Creates a curve tween.
  ///
  /// The [curve] argument must not be null.
315 316
  CurveTween({ @required this.curve })
    : assert(curve != null);
317

318
  /// The curve to use when transforming the value of the animation.
319 320
  Curve curve;

321
  @override
322
  double evaluate(Animation<double> animation) {
323
    final double t = animation.value;
324 325 326 327 328 329
    if (t == 0.0 || t == 1.0) {
      assert(curve.transform(t).round() == t);
      return t;
    }
    return curve.transform(t);
  }
330 331 332

  @override
  String toString() => '$runtimeType(curve: $curve)';
333
}