curves.dart 6.19 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.

5 6
import 'dart:math' as math;

7 8 9 10 11 12
double _evaluateCubic(double a, double b, double m) {
  return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m;
}

const double _kCubicErrorBound = 0.001;

13
/// A mapping of the unit interval to the unit interval.
14 15
///
/// A curve must map 0.0 to 0.0 and 1.0 to 1.0.
16 17
///
/// See [Curves] for a collection of common animation curves.
18
abstract class Curve {
Florian Loitsch's avatar
Florian Loitsch committed
19
  /// Returns the value of the curve at point [t].
20
  ///
Florian Loitsch's avatar
Florian Loitsch committed
21
  /// The value of [t] must be between 0.0 and 1.0, inclusive.
22 23 24
  double transform(double t);
}

Florian Loitsch's avatar
Florian Loitsch committed
25
/// The identity map over the unit interval.
26 27
class Linear implements Curve {
  const Linear();
28
  double transform(double t) => t;
29 30
}

Florian Loitsch's avatar
Florian Loitsch committed
31
/// A curve that is 0.0 until start, then curved from 0.0 to 1.0 at end, then 1.0.
32
class Interval implements Curve {
33
  const Interval(this.start, this.end, { this.curve: Curves.linear });
34

Florian Loitsch's avatar
Florian Loitsch committed
35
  /// The smallest value for which this interval is 0.0.
36 37
  final double start;

Florian Loitsch's avatar
Florian Loitsch committed
38
  /// The smallest value for which this interval is 1.0.
39 40
  final double end;

Florian Loitsch's avatar
Florian Loitsch committed
41
  /// The curve to apply between [start] and [end].
42 43
  final Curve curve;

44
  double transform(double t) {
45 46 47 48
    assert(start >= 0.0);
    assert(start <= 1.0);
    assert(end >= 0.0);
    assert(end <= 1.0);
Hixie's avatar
Hixie committed
49
    assert(end >= start);
50 51 52 53
    t = ((t - start) / (end - start)).clamp(0.0, 1.0);
    if (t == 0.0 || t == 1.0)
      return t;
    return curve.transform(t);
54 55 56
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
57
/// A cubic polynomial mapping of the unit interval.
58
class Cubic implements Curve {
59 60
  const Cubic(this.a, this.b, this.c, this.d);

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
  final double a;
  final double b;
  final double c;
  final double d;

  double transform(double t) {
    double start = 0.0;
    double end = 1.0;
    while (true) {
      double midpoint = (start + end) / 2;
      double estimate = _evaluateCubic(a, c, midpoint);
      if ((t - estimate).abs() < _kCubicErrorBound)
        return _evaluateCubic(b, d, midpoint);
      if (estimate < t)
        start = midpoint;
      else
        end = midpoint;
    }
  }
}

82
double _bounce(double t) {
83 84 85 86 87 88 89 90 91 92 93 94 95
  if (t < 1.0 / 2.75) {
    return 7.5625 * t * t;
  } else if (t < 2 / 2.75) {
    t -= 1.5 / 2.75;
    return 7.5625 * t * t + 0.75;
  } else if (t < 2.5 / 2.75) {
    t -= 2.25 / 2.75;
    return 7.5625 * t * t + 0.9375;
  }
  t -= 2.625 / 2.75;
  return 7.5625 * t * t + 0.984375;
}

Florian Loitsch's avatar
Florian Loitsch committed
96
/// An oscillating curve that grows in magnitude.
97 98 99 100 101 102 103
class BounceInCurve implements Curve {
  const BounceInCurve();
  double transform(double t) {
    return 1.0 - _bounce(1.0 - t);
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
104
/// An oscillating curve that shrink in magnitude.
105 106 107 108 109 110 111
class BounceOutCurve implements Curve {
  const BounceOutCurve();
  double transform(double t) {
    return _bounce(t);
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
112
/// An oscillating curve that first grows and then shrink in magnitude.
113 114 115 116 117 118 119 120 121 122
class BounceInOutCurve implements Curve {
  const BounceInOutCurve();
  double transform(double t) {
    if (t < 0.5)
      return (1.0 - _bounce(1.0 - t)) * 0.5;
    else
      return _bounce(t * 2.0 - 1.0) * 0.5 + 0.5;
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
123
/// An oscillating curve that grows in magnitude while overshooting its bounds.
124 125 126 127 128 129 130 131 132 133
class ElasticInCurve implements Curve {
  const ElasticInCurve([this.period = 0.4]);
  final double period;
  double transform(double t) {
    double s = period / 4.0;
    t = t - 1.0;
    return -math.pow(2.0, 10.0 * t) * math.sin((t - s) * (math.PI * 2.0) / period);
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
134
/// An oscillating curve that shrinks in magnitude while overshooting its bounds.
135 136 137 138 139 140 141 142 143
class ElasticOutCurve implements Curve {
  const ElasticOutCurve([this.period = 0.4]);
  final double period;
  double transform(double t) {
    double s = period / 4.0;
    return math.pow(2.0, -10 * t) * math.sin((t - s) * (math.PI * 2.0) / period) + 1.0;
  }
}

Florian Loitsch's avatar
Florian Loitsch committed
144
/// An oscillating curve that grows and then shrinks in magnitude while overshooting its bounds.
145 146 147 148 149
class ElasticInOutCurve implements Curve {
  const ElasticInOutCurve([this.period = 0.4]);
  final double period;
  double transform(double t) {
    double s = period / 4.0;
150
    t = 2.0 * t - 1.0;
151 152 153 154 155 156 157
    if (t < 0.0)
      return -0.5 * math.pow(2.0, 10.0 * t) * math.sin((t - s) * (math.PI * 2.0) / period);
    else
      return math.pow(2.0, -10.0 * t) * math.sin((t - s) * (math.PI * 2.0) / period) * 0.5 + 1.0;
  }
}

158 159 160
/// A collection of common animation curves.
class Curves {
  Curves._();
161

162 163
  /// A linear animation curve
  static const Linear linear = const Linear();
164

Florian Loitsch's avatar
Florian Loitsch committed
165
  /// A cubic animation curve that speeds up quickly and ends slowly.
166
  static const Cubic ease = const Cubic(0.25, 0.1, 0.25, 1.0);
167

Florian Loitsch's avatar
Florian Loitsch committed
168
  /// A cubic animation curve that starts slowly and ends quickly.
169
  static const Cubic easeIn = const Cubic(0.42, 0.0, 1.0, 1.0);
170

Florian Loitsch's avatar
Florian Loitsch committed
171
  /// A cubic animation curve that starts quickly and ends slowly.
172
  static const Cubic easeOut = const Cubic(0.0, 0.0, 0.58, 1.0);
173

Florian Loitsch's avatar
Florian Loitsch committed
174
  /// A cubic animation curve that starts slowly, speeds up, and then and ends slowly.
175
  static const Cubic easeInOut = const Cubic(0.42, 0.0, 0.58, 1.0);
176

Florian Loitsch's avatar
Florian Loitsch committed
177
  /// An oscillating curve that grows in magnitude.
178
  static const BounceInCurve bounceIn = const BounceInCurve();
179

Florian Loitsch's avatar
Florian Loitsch committed
180
  /// An oscillating curve that first grows and then shrink in magnitude.
181
  static const BounceOutCurve bounceOut = const BounceOutCurve();
182

Florian Loitsch's avatar
Florian Loitsch committed
183
  /// An oscillating curve that first grows and then shrink in magnitude.
184
  static const BounceInOutCurve bounceInOut = const BounceInOutCurve();
185

Florian Loitsch's avatar
Florian Loitsch committed
186
  /// An oscillating curve that grows in magnitude while overshootings its bounds.
187
  static const ElasticInCurve elasticIn = const ElasticInCurve();
188

Florian Loitsch's avatar
Florian Loitsch committed
189
  /// An oscillating curve that shrinks in magnitude while overshootings its bounds.
190
  static const ElasticOutCurve elasticOut = const ElasticOutCurve();
191

Florian Loitsch's avatar
Florian Loitsch committed
192
  /// An oscillating curve that grows and then shrinks in magnitude while overshootings its bounds.
193 194
  static const ElasticInOutCurve elasticInOut = const ElasticInOutCurve();

Florian Loitsch's avatar
Florian Loitsch committed
195 196 197 198 199
  /// A curve that starts quickly and eases into its final position.
  ///
  /// Over the course of the animation, the object spends more time near its
  /// final destination. As a result, the user isn’t left waiting for the
  /// animation to finish, and the negative effects of motion are minimized.
200 201
  static const Curve fastOutSlowIn = const Cubic(0.4, 0.0, 0.2, 1.0);
}