blob: 2285b0021c7da5653de9976648650ef0495afe96 [file] [log] [blame]
// 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.
import 'dart:ui' show Color, Size, Rect, hashValues;
import 'package:flutter/foundation.dart';
import 'animation.dart';
import 'animations.dart';
import 'curves.dart';
/// 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.
abstract class Animatable<T> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const Animatable();
/// The current value of this object for the given animation.
T evaluate(Animation<double> animation);
/// Returns a new Animation that is driven by the given animation but that
/// takes on values determined by this object.
Animation<T> animate(Animation<double> parent) {
return new _AnimatedEvaluation<T>(parent, this);
}
/// Returns a new Animatable whose value is determined by first evaluating
/// the given parent and then evaluating this object.
Animatable<T> chain(Animatable<double> parent) {
return new _ChainedEvaluation<T>(parent, this);
}
}
class _AnimatedEvaluation<T> extends Animation<T> with AnimationWithParentMixin<double> {
_AnimatedEvaluation(this.parent, this._evaluatable);
@override
final Animation<double> parent;
final Animatable<T> _evaluatable;
@override
T get value => _evaluatable.evaluate(parent);
@override
String toString() {
return '$parent\u27A9$_evaluatable\u27A9$value';
}
@override
String toStringDetails() {
return '${super.toStringDetails()} $_evaluatable';
}
}
class _ChainedEvaluation<T> extends Animatable<T> {
_ChainedEvaluation(this._parent, this._evaluatable);
final Animatable<double> _parent;
final Animatable<T> _evaluatable;
@override
T evaluate(Animation<double> animation) {
final double value = _parent.evaluate(animation);
return _evaluatable.evaluate(new AlwaysStoppedAnimation<double>(value));
}
@override
String toString() {
return '$_parent\u27A9$_evaluatable';
}
}
/// A linear interpolation between a beginning and ending value.
///
/// [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].
///
/// ## Sample code
///
/// 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
/// Animation<Offset> _animation = new Tween<Offset>(
/// begin: const Offset(100.0, 50.0),
/// end: const Offset(200.0, 300.0),
/// ).animate(_controller);
/// ```
///
/// 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.
class Tween<T extends dynamic> extends Animatable<T> {
/// Creates a tween.
///
/// 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.
Tween({ this.begin, this.end });
/// The value this variable has at the beginning of the animation.
///
/// See the constructor for details about whether this property may be null
/// (it varies from subclass to subclass).
T begin;
/// The value this variable has at the end of the animation.
///
/// See the constructor for details about whether this property may be null
/// (it varies from subclass to subclass).
T end;
/// Returns the value this variable has at the given animation clock value.
///
/// 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;
}
/// Returns the interpolated value for the current value of the given animation.
///
/// This method returns `begin` and `end` when the animation values are 0.0 or
/// 1.0, respectively.
///
/// 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.
@override
T evaluate(Animation<double> animation) {
final double t = animation.value;
if (t == 0.0)
return begin;
if (t == 1.0)
return end;
return lerp(t);
}
@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);
@override
String toString() => '$runtimeType($begin \u2192 $end)';
}
/// 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);
}
/// An interpolation between two colors.
///
/// This class specializes the interpolation of [Tween<Color>] to use
/// [Color.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class ColorTween extends Tween<Color> {
/// Creates a [Color] tween.
///
/// The [begin] and [end] properties may be null; the null value
/// is treated as transparent black.
ColorTween({ Color begin, Color end }) : super(begin: begin, end: end);
/// Returns the value this variable has at the given animation clock value.
@override
Color lerp(double t) => Color.lerp(begin, end, t);
}
/// An interpolation between two sizes.
///
/// This class specializes the interpolation of [Tween<Size>] to use
/// [Size.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class SizeTween extends Tween<Size> {
/// Creates a [Size] tween.
///
/// The [begin] and [end] properties may be null; the null value
/// is treated as an empty size.
SizeTween({ Size begin, Size end }) : super(begin: begin, end: end);
/// Returns the value this variable has at the given animation clock value.
@override
Size lerp(double t) => Size.lerp(begin, end, t);
}
/// An interpolation between two rectangles.
///
/// This class specializes the interpolation of [Tween<Rect>] to use
/// [Rect.lerp].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class RectTween extends Tween<Rect> {
/// Creates a [Rect] tween.
///
/// The [begin] and [end] properties may be null; the null value
/// is treated as an empty rect at the top left corner.
RectTween({ Rect begin, Rect end }) : super(begin: begin, end: end);
/// Returns the value this variable has at the given animation clock value.
@override
Rect lerp(double t) => Rect.lerp(begin, end, t);
}
/// An interpolation between two integers that rounds.
///
/// This class specializes the interpolation of [Tween<int>] to be
/// 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] and [Tween<double>].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class IntTween extends Tween<int> {
/// Creates an int tween.
///
/// 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.
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.
@override
int lerp(double t) => (begin + (end - begin) * t).round();
}
/// 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].
///
/// See [Tween] for a discussion on how to use interpolation objects.
class StepTween extends Tween<int> {
/// Creates an [int] tween that floors.
///
/// 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.
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.
@override
int lerp(double t) => (begin + (end - begin) * t).floor();
}
/// 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].
class CurveTween extends Animatable<double> {
/// Creates a curve tween.
///
/// The [curve] argument must not be null.
CurveTween({ @required this.curve })
: assert(curve != null);
/// The curve to use when transforming the value of the animation.
Curve curve;
@override
double evaluate(Animation<double> animation) {
final double t = animation.value;
if (t == 0.0 || t == 1.0) {
assert(curve.transform(t).round() == t);
return t;
}
return curve.transform(t);
}
}