1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/material.dart';
6library;
7
8import 'dart:math' as math;
9import 'dart:ui';
10
11import 'package:flutter/cupertino.dart';
12import 'package:flutter/foundation.dart';
13
14export 'dart:ui' show Offset;
15
16/// An abstract class providing an interface for evaluating a parametric curve.
17///
18/// A parametric curve transforms a parameter (hence the name) `t` along a curve
19/// to the value of the curve at that value of `t`. The curve can be of
20/// arbitrary dimension, but is typically a 1D, 2D, or 3D curve.
21///
22/// See also:
23///
24/// * [Curve], a 1D animation easing curve that starts at 0.0 and ends at 1.0.
25/// * [Curve2D], a parametric curve that transforms the parameter to a 2D point.
26abstract class ParametricCurve<T> {
27 /// Abstract const constructor to enable subclasses to provide
28 /// const constructors so that they can be used in const expressions.
29 const ParametricCurve();
30
31 /// Returns the value of the curve at point `t`.
32 ///
33 /// This method asserts that t is between 0 and 1 before delegating to
34 /// [transformInternal].
35 ///
36 /// It is recommended that subclasses override [transformInternal] instead of
37 /// this function, as the above case is already handled in the default
38 /// implementation of [transform], which delegates the remaining logic to
39 /// [transformInternal].
40 T transform(double t) {
41 assert(t >= 0.0 && t <= 1.0, 'parametric value $t is outside of [0, 1] range.');
42 return transformInternal(t);
43 }
44
45 /// Returns the value of the curve at point `t`.
46 ///
47 /// The given parametric value `t` will be between 0.0 and 1.0, inclusive.
48 @protected
49 T transformInternal(double t) {
50 throw UnimplementedError();
51 }
52
53 @override
54 String toString() => objectRuntimeType(this, 'ParametricCurve');
55}
56
57/// An parametric animation easing curve, i.e. a mapping of the unit interval to
58/// the unit interval.
59///
60/// Easing curves are used to adjust the rate of change of an animation over
61/// time, allowing them to speed up and slow down, rather than moving at a
62/// constant rate.
63///
64/// A [Curve] must map t=0.0 to 0.0 and t=1.0 to 1.0.
65///
66/// See also:
67///
68/// * [Curves], a collection of common animation easing curves.
69/// * [CurveTween], which can be used to apply a [Curve] to an [Animation].
70/// * [Canvas.drawArc], which draws an arc, and has nothing to do with easing
71/// curves.
72/// * [Animatable], for a more flexible interface that maps fractions to
73/// arbitrary values.
74@immutable
75abstract class Curve extends ParametricCurve<double> {
76 /// Abstract const constructor to enable subclasses to provide
77 /// const constructors so that they can be used in const expressions.
78 const Curve();
79
80 /// Returns the value of the curve at point `t`.
81 ///
82 /// This function must ensure the following:
83 /// - The value of `t` must be between 0.0 and 1.0
84 /// - Values of `t`=0.0 and `t`=1.0 must be mapped to 0.0 and 1.0,
85 /// respectively.
86 ///
87 /// It is recommended that subclasses override [transformInternal] instead of
88 /// this function, as the above cases are already handled in the default
89 /// implementation of [transform], which delegates the remaining logic to
90 /// [transformInternal].
91 @override
92 double transform(double t) {
93 if (t == 0.0 || t == 1.0) {
94 return t;
95 }
96 return super.transform(t);
97 }
98
99 /// Returns a new curve that is the reversed inversion of this one.
100 ///
101 /// This is often useful with [CurvedAnimation.reverseCurve].
102 ///
103 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
104 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped.mp4}
105 ///
106 /// See also:
107 ///
108 /// * [FlippedCurve], the class that is used to implement this getter.
109 /// * [ReverseAnimation], which reverses an [Animation] rather than a [Curve].
110 /// * [CurvedAnimation], which can take a separate curve and reverse curve.
111 Curve get flipped => FlippedCurve(this);
112}
113
114/// The identity map over the unit interval.
115///
116/// See [Curves.linear] for an instance of this class.
117class _Linear extends Curve {
118 const _Linear._();
119
120 @override
121 double transformInternal(double t) => t;
122}
123
124/// A sawtooth curve that repeats a given number of times over the unit interval.
125///
126/// The curve rises linearly from 0.0 to 1.0 and then falls discontinuously back
127/// to 0.0 each iteration.
128///
129/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_sawtooth.mp4}
130class SawTooth extends Curve {
131 /// Creates a sawtooth curve.
132 const SawTooth(this.count);
133
134 /// The number of repetitions of the sawtooth pattern in the unit interval.
135 final int count;
136
137 @override
138 double transformInternal(double t) {
139 t *= count;
140 return t - t.truncateToDouble();
141 }
142
143 @override
144 String toString() {
145 return '${objectRuntimeType(this, 'SawTooth')}($count)';
146 }
147}
148
149/// A curve that is 0.0 until [begin], then curved (according to [curve]) from
150/// 0.0 at [begin] to 1.0 at [end], then remains 1.0 past [end].
151///
152/// An [Interval] can be used to delay an animation. For example, a six second
153/// animation that uses an [Interval] with its [begin] set to 0.5 and its [end]
154/// set to 1.0 will essentially become a three-second animation that starts
155/// three seconds later.
156///
157/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_interval.mp4}
158class Interval extends Curve {
159 /// Creates an interval curve.
160 const Interval(this.begin, this.end, {this.curve = Curves.linear});
161
162 /// The largest value for which this interval is 0.0.
163 ///
164 /// From t=0.0 to t=[begin], the interval's value is 0.0.
165 final double begin;
166
167 /// The smallest value for which this interval is 1.0.
168 ///
169 /// From t=[end] to t=1.0, the interval's value is 1.0.
170 final double end;
171
172 /// The curve to apply between [begin] and [end].
173 final Curve curve;
174
175 @override
176 double transformInternal(double t) {
177 assert(begin >= 0.0);
178 assert(begin <= 1.0);
179 assert(end >= 0.0);
180 assert(end <= 1.0);
181 assert(end >= begin);
182 t = clampDouble((t - begin) / (end - begin), 0.0, 1.0);
183 if (t == 0.0 || t == 1.0) {
184 return t;
185 }
186 return curve.transform(t);
187 }
188
189 @override
190 String toString() {
191 if (curve is! _Linear) {
192 return '${objectRuntimeType(this, 'Interval')}($begin\u22EF$end)\u27A9$curve';
193 }
194 return '${objectRuntimeType(this, 'Interval')}($begin\u22EF$end)';
195 }
196}
197
198/// A curve that progresses according to [beginCurve] until [split], then
199/// according to [endCurve].
200///
201/// Split curves are useful in situations where a widget must track the
202/// user's finger (which requires a linear animation), but can also be flung
203/// using a curve specified with the [endCurve] argument, after the finger is
204/// released. In such a case, the value of [split] would be the progress
205/// of the animation at the time when the finger was released.
206///
207/// For example, if [split] is set to 0.5, [beginCurve] is [Curves.linear],
208/// and [endCurve] is [Curves.easeOutCubic], then the bottom-left quarter of the
209/// curve will be a straight line, and the top-right quarter will contain the
210/// entire [Curves.easeOutCubic] curve.
211///
212/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_split.mp4}
213class Split extends Curve {
214 /// Creates a split curve.
215 const Split(this.split, {this.beginCurve = Curves.linear, this.endCurve = Curves.easeOutCubic});
216
217 /// The progress value separating [beginCurve] from [endCurve].
218 ///
219 /// The value before which the curve progresses according to [beginCurve] and
220 /// after which the curve progresses according to [endCurve].
221 ///
222 /// When t is exactly `split`, the curve has the value `split`.
223 ///
224 /// Must be between 0 and 1.0, inclusively.
225 final double split;
226
227 /// The curve to use before [split] is reached.
228 ///
229 /// Defaults to [Curves.linear].
230 final Curve beginCurve;
231
232 /// The curve to use after [split] is reached.
233 ///
234 /// Defaults to [Curves.easeOutCubic].
235 final Curve endCurve;
236
237 @override
238 double transform(double t) {
239 assert(t >= 0.0 && t <= 1.0);
240 assert(split >= 0.0 && split <= 1.0);
241
242 if (t == 0.0 || t == 1.0) {
243 return t;
244 }
245
246 if (t == split) {
247 return split;
248 }
249
250 if (t < split) {
251 final double curveProgress = t / split;
252 final double transformed = beginCurve.transform(curveProgress);
253 return lerpDouble(0, split, transformed)!;
254 } else {
255 final double curveProgress = (t - split) / (1 - split);
256 final double transformed = endCurve.transform(curveProgress);
257 return lerpDouble(split, 1, transformed)!;
258 }
259 }
260
261 @override
262 String toString() {
263 return '${describeIdentity(this)}($split, $beginCurve, $endCurve)';
264 }
265}
266
267/// A curve that is 0.0 until it hits the threshold, then it jumps to 1.0.
268///
269/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_threshold.mp4}
270class Threshold extends Curve {
271 /// Creates a threshold curve.
272 const Threshold(this.threshold);
273
274 /// The value before which the curve is 0.0 and after which the curve is 1.0.
275 ///
276 /// When t is exactly [threshold], the curve has the value 1.0.
277 final double threshold;
278
279 @override
280 double transformInternal(double t) {
281 assert(threshold >= 0.0);
282 assert(threshold <= 1.0);
283 return t < threshold ? 0.0 : 1.0;
284 }
285}
286
287/// A cubic polynomial mapping of the unit interval.
288///
289/// The [Curves] class contains some commonly used cubic curves:
290///
291/// * [Curves.fastLinearToSlowEaseIn]
292/// * [Curves.ease]
293/// * [Curves.easeIn]
294/// * [Curves.easeInToLinear]
295/// * [Curves.easeInSine]
296/// * [Curves.easeInQuad]
297/// * [Curves.easeInCubic]
298/// * [Curves.easeInQuart]
299/// * [Curves.easeInQuint]
300/// * [Curves.easeInExpo]
301/// * [Curves.easeInCirc]
302/// * [Curves.easeInBack]
303/// * [Curves.easeOut]
304/// * [Curves.linearToEaseOut]
305/// * [Curves.easeOutSine]
306/// * [Curves.easeOutQuad]
307/// * [Curves.easeOutCubic]
308/// * [Curves.easeOutQuart]
309/// * [Curves.easeOutQuint]
310/// * [Curves.easeOutExpo]
311/// * [Curves.easeOutCirc]
312/// * [Curves.easeOutBack]
313/// * [Curves.easeInOut]
314/// * [Curves.easeInOutSine]
315/// * [Curves.easeInOutQuad]
316/// * [Curves.easeInOutCubic]
317/// * [Curves.easeInOutQuart]
318/// * [Curves.easeInOutQuint]
319/// * [Curves.easeInOutExpo]
320/// * [Curves.easeInOutCirc]
321/// * [Curves.easeInOutBack]
322/// * [Curves.fastOutSlowIn]
323/// * [Curves.slowMiddle]
324///
325/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_linear_to_slow_ease_in.mp4}
326/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
327/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4}
328/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_to_linear.mp4}
329/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_sine.mp4}
330/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quad.mp4}
331/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_cubic.mp4}
332/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quart.mp4}
333/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quint.mp4}
334/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_expo.mp4}
335/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_circ.mp4}
336/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_back.mp4}
337/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4}
338/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear_to_ease_out.mp4}
339/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_sine.mp4}
340/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quad.mp4}
341/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_cubic.mp4}
342/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quart.mp4}
343/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quint.mp4}
344/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_expo.mp4}
345/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_circ.mp4}
346/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_back.mp4}
347/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out.mp4}
348/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_sine.mp4}
349/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quad.mp4}
350/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic.mp4}
351/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quart.mp4}
352/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quint.mp4}
353/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_expo.mp4}
354/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_circ.mp4}
355/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_back.mp4}
356/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_out_slow_in.mp4}
357/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_slow_middle.mp4}
358///
359/// The [Cubic] class implements third-order Bézier curves.
360///
361/// See also:
362///
363/// * [Curves], where many more predefined curves are available.
364/// * [CatmullRomCurve], a curve which passes through specific values.
365class Cubic extends Curve {
366 /// Creates a cubic curve.
367 ///
368 /// Rather than creating a new instance, consider using one of the common
369 /// cubic curves in [Curves].
370 const Cubic(this.a, this.b, this.c, this.d);
371
372 /// The x coordinate of the first control point.
373 ///
374 /// The line through the point (0, 0) and the first control point is tangent
375 /// to the curve at the point (0, 0).
376 final double a;
377
378 /// The y coordinate of the first control point.
379 ///
380 /// The line through the point (0, 0) and the first control point is tangent
381 /// to the curve at the point (0, 0).
382 final double b;
383
384 /// The x coordinate of the second control point.
385 ///
386 /// The line through the point (1, 1) and the second control point is tangent
387 /// to the curve at the point (1, 1).
388 final double c;
389
390 /// The y coordinate of the second control point.
391 ///
392 /// The line through the point (1, 1) and the second control point is tangent
393 /// to the curve at the point (1, 1).
394 final double d;
395
396 static const double _cubicErrorBound = 0.001;
397
398 double _evaluateCubic(double a, double b, double m) {
399 return 3 * a * (1 - m) * (1 - m) * m + 3 * b * (1 - m) * m * m + m * m * m;
400 }
401
402 @override
403 double transformInternal(double t) {
404 double start = 0.0;
405 double end = 1.0;
406 while (true) {
407 final double midpoint = (start + end) / 2;
408 final double estimate = _evaluateCubic(a, c, midpoint);
409 if ((t - estimate).abs() < _cubicErrorBound) {
410 return _evaluateCubic(b, d, midpoint);
411 }
412 if (estimate < t) {
413 start = midpoint;
414 } else {
415 end = midpoint;
416 }
417 }
418 }
419
420 @override
421 String toString() {
422 return '${objectRuntimeType(this, 'Cubic')}(${a.toStringAsFixed(2)}, ${b.toStringAsFixed(2)}, ${c.toStringAsFixed(2)}, ${d.toStringAsFixed(2)})';
423 }
424}
425
426/// A cubic polynomial composed of two curves that share a common center point.
427///
428/// The curve runs through three points: (0,0), the [midpoint], and (1,1).
429///
430/// The [Curves] class contains a curve defined with this class:
431/// [Curves.easeInOutCubicEmphasized].
432///
433/// The [ThreePointCubic] class implements third-order Bézier curves, where two
434/// curves share an interior [midpoint] that the curve passes through. If the
435/// control points surrounding the middle point ([b1], and [a2]) are not
436/// colinear with the middle point, then the curve's derivative will have a
437/// discontinuity (a cusp) at the shared middle point.
438///
439/// See also:
440///
441/// * [Curves], where many more predefined curves are available.
442/// * [Cubic], which defines a single cubic polynomial.
443/// * [CatmullRomCurve], a curve which passes through specific values.
444class ThreePointCubic extends Curve {
445 /// Creates two cubic curves that share a common control point.
446 ///
447 /// Rather than creating a new instance, consider using one of the common
448 /// three-point cubic curves in [Curves].
449 ///
450 /// The arguments correspond to the control points for the two curves,
451 /// including the [midpoint], but do not include the two implied end points at
452 /// (0,0) and (1,1), which are fixed.
453 const ThreePointCubic(this.a1, this.b1, this.midpoint, this.a2, this.b2);
454
455 /// The coordinates of the first control point of the first curve.
456 ///
457 /// The line through the point (0, 0) and this control point is tangent to the
458 /// curve at the point (0, 0).
459 final Offset a1;
460
461 /// The coordinates of the second control point of the first curve.
462 ///
463 /// The line through the [midpoint] and this control point is tangent to the
464 /// curve approaching the [midpoint].
465 final Offset b1;
466
467 /// The coordinates of the middle shared point.
468 ///
469 /// The curve will go through this point. If the control points surrounding
470 /// this middle point ([b1], and [a2]) are not colinear with this point, then
471 /// the curve's derivative will have a discontinuity (a cusp) at this point.
472 final Offset midpoint;
473
474 /// The coordinates of the first control point of the second curve.
475 ///
476 /// The line through the [midpoint] and this control point is tangent to the
477 /// curve approaching the [midpoint].
478 final Offset a2;
479
480 /// The coordinates of the second control point of the second curve.
481 ///
482 /// The line through the point (1, 1) and this control point is tangent to the
483 /// curve at (1, 1).
484 final Offset b2;
485
486 @override
487 double transformInternal(double t) {
488 final bool firstCurve = t < midpoint.dx;
489 final double scaleX = firstCurve ? midpoint.dx : 1.0 - midpoint.dx;
490 final double scaleY = firstCurve ? midpoint.dy : 1.0 - midpoint.dy;
491 final double scaledT = (t - (firstCurve ? 0.0 : midpoint.dx)) / scaleX;
492 if (firstCurve) {
493 return Cubic(
494 a1.dx / scaleX,
495 a1.dy / scaleY,
496 b1.dx / scaleX,
497 b1.dy / scaleY,
498 ).transform(scaledT) *
499 scaleY;
500 } else {
501 return Cubic(
502 (a2.dx - midpoint.dx) / scaleX,
503 (a2.dy - midpoint.dy) / scaleY,
504 (b2.dx - midpoint.dx) / scaleX,
505 (b2.dy - midpoint.dy) / scaleY,
506 ).transform(scaledT) *
507 scaleY +
508 midpoint.dy;
509 }
510 }
511
512 @override
513 String toString() {
514 return '${objectRuntimeType(this, 'ThreePointCubic($a1, $b1, $midpoint, $a2, $b2)')} ';
515 }
516}
517
518/// Abstract class that defines an API for evaluating 2D parametric curves.
519///
520/// [Curve2D] differs from [Curve] in that the values interpolated are [Offset]
521/// values instead of [double] values, hence the "2D" in the name. They both
522/// take a single double `t` that has a range of 0.0 to 1.0, inclusive, as input
523/// to the [transform] function . Unlike [Curve], [Curve2D] is not required to
524/// map `t=0.0` and `t=1.0` to specific output values.
525///
526/// The interpolated `t` value given to [transform] represents the progression
527/// along the curve, but it doesn't necessarily progress at a constant velocity, so
528/// incrementing `t` by, say, 0.1 might move along the curve by quite a lot at one
529/// part of the curve, or hardly at all in another part of the curve, depending
530/// on the definition of the curve.
531///
532/// {@tool dartpad}
533/// This example shows how to use a [Curve2D] to modify the position of a widget
534/// so that it can follow an arbitrary path.
535///
536/// ** See code in examples/api/lib/animation/curves/curve2_d.0.dart **
537/// {@end-tool}
538///
539abstract class Curve2D extends ParametricCurve<Offset> {
540 /// Abstract const constructor to enable subclasses to provide const
541 /// constructors so that they can be used in const expressions.
542 const Curve2D();
543
544 /// Generates a list of samples with a recursive subdivision until a tolerance
545 /// of `tolerance` is reached.
546 ///
547 /// Samples are generated in order.
548 ///
549 /// Samples can be used to render a curve efficiently, since the samples
550 /// constitute line segments which vary in size with the curvature of the
551 /// curve. They can also be used to quickly approximate the value of the curve
552 /// by searching for the desired range in X and linearly interpolating between
553 /// samples to obtain an approximation of Y at the desired X value. The
554 /// implementation of [CatmullRomCurve] uses samples for this purpose
555 /// internally.
556 ///
557 /// The tolerance is computed as the area of a triangle formed by a new point
558 /// and the preceding and following point.
559 ///
560 /// See also:
561 ///
562 /// * Luiz Henrique de Figueire's Graphics Gem on [the algorithm](http://ariel.chronotext.org/dd/defigueiredo93adaptive.pdf).
563 Iterable<Curve2DSample> generateSamples({
564 double start = 0.0,
565 double end = 1.0,
566 double tolerance = 1e-10,
567 }) {
568 // The sampling algorithm is:
569 // 1. Evaluate the area of the triangle (a proxy for the "flatness" of the
570 // curve) formed by two points and a test point.
571 // 2. If the area of the triangle is small enough (below tolerance), then
572 // the two points form the final segment.
573 // 3. If the area is still too large, divide the interval into two parts
574 // using a random subdivision point to avoid aliasing.
575 // 4. Recursively sample the two parts.
576 //
577 // This algorithm concentrates samples in areas of high curvature.
578 assert(end > start);
579 // We want to pick a random seed that will keep the result stable if
580 // evaluated again, so we use the first non-generated control point.
581 final math.Random rand = math.Random(samplingSeed);
582 bool isFlat(Offset p, Offset q, Offset r) {
583 // Calculates the area of the triangle given by the three points.
584 final Offset pr = p - r;
585 final Offset qr = q - r;
586 final double z = pr.dx * qr.dy - qr.dx * pr.dy;
587 return (z * z) < tolerance;
588 }
589
590 final Curve2DSample first = Curve2DSample(start, transform(start));
591 final Curve2DSample last = Curve2DSample(end, transform(end));
592 final List<Curve2DSample> samples = <Curve2DSample>[first];
593 void sample(Curve2DSample p, Curve2DSample q, {bool forceSubdivide = false}) {
594 // Pick a random point somewhat near the center, which avoids aliasing
595 // problems with periodic curves.
596 final double t = p.t + (0.45 + 0.1 * rand.nextDouble()) * (q.t - p.t);
597 final Curve2DSample r = Curve2DSample(t, transform(t));
598
599 if (!forceSubdivide && isFlat(p.value, q.value, r.value)) {
600 samples.add(q);
601 } else {
602 sample(p, r);
603 sample(r, q);
604 }
605 }
606
607 // If the curve starts and ends on the same point, then we force it to
608 // subdivide at least once, because otherwise it will terminate immediately.
609 sample(
610 first,
611 last,
612 forceSubdivide:
613 (first.value.dx - last.value.dx).abs() < tolerance &&
614 (first.value.dy - last.value.dy).abs() < tolerance,
615 );
616 return samples;
617 }
618
619 /// Returns a seed value used by [generateSamples] to seed a random number
620 /// generator to avoid sample aliasing.
621 ///
622 /// Subclasses should override this and provide a custom seed.
623 ///
624 /// The value returned should be the same each time it is called, unless the
625 /// curve definition changes.
626 @protected
627 int get samplingSeed => 0;
628
629 /// Returns the parameter `t` that corresponds to the given x value of the spline.
630 ///
631 /// This will only work properly for curves which are single-valued in x
632 /// (where every value of `x` maps to only one value in 'y', i.e. the curve
633 /// does not loop or curve back over itself). For curves that are not
634 /// single-valued, it will return the parameter for only one of the values at
635 /// the given `x` location.
636 double findInverse(double x) {
637 double start = 0.0;
638 double end = 1.0;
639 late double mid;
640 double offsetToOrigin(double pos) => x - transform(pos).dx;
641 // Use a binary search to find the inverse point within 1e-6, or 100
642 // subdivisions, whichever comes first.
643 const double errorLimit = 1e-6;
644 int count = 100;
645 final double startValue = offsetToOrigin(start);
646 while ((end - start) / 2.0 > errorLimit && count > 0) {
647 mid = (end + start) / 2.0;
648 final double value = offsetToOrigin(mid);
649 if (value.sign == startValue.sign) {
650 start = mid;
651 } else {
652 end = mid;
653 }
654 count--;
655 }
656 return mid;
657 }
658}
659
660/// A class that holds a sample of a 2D parametric curve, containing the [value]
661/// (the X, Y coordinates) of the curve at the parametric value [t].
662///
663/// See also:
664///
665/// * [Curve2D.generateSamples], which generates samples of this type.
666/// * [Curve2D], a parametric curve that maps a double parameter to a 2D location.
667class Curve2DSample {
668 /// Creates an object that holds a sample; used with [Curve2D] subclasses.
669 const Curve2DSample(this.t, this.value);
670
671 /// The parametric location of this sample point along the curve.
672 final double t;
673
674 /// The value (the X, Y coordinates) of the curve at parametric value [t].
675 final Offset value;
676
677 @override
678 String toString() {
679 return '[(${value.dx.toStringAsFixed(2)}, ${value.dy.toStringAsFixed(2)}), ${t.toStringAsFixed(2)}]';
680 }
681}
682
683/// A 2D spline that passes smoothly through the given control points using a
684/// centripetal Catmull-Rom spline.
685///
686/// When the curve is evaluated with [transform], the output values will move
687/// smoothly from one control point to the next, passing through the control
688/// points.
689///
690/// {@template flutter.animation.CatmullRomSpline}
691/// Unlike most cubic splines, Catmull-Rom splines have the advantage that their
692/// curves pass through the control points given to them. They are cubic
693/// polynomial representations, and, in fact, Catmull-Rom splines can be
694/// converted mathematically into cubic splines. This class implements a
695/// "centripetal" Catmull-Rom spline. The term centripetal implies that it won't
696/// form loops or self-intersections within a single segment.
697/// {@endtemplate}
698///
699/// See also:
700/// * [Centripetal Catmull–Rom splines](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline)
701/// on Wikipedia.
702/// * [Parameterization and Applications of Catmull-Rom Curves](http://faculty.cs.tamu.edu/schaefer/research/cr_cad.pdf),
703/// a paper on using Catmull-Rom splines.
704/// * [CatmullRomCurve], an animation curve that uses a [CatmullRomSpline] as its
705/// internal representation.
706class CatmullRomSpline extends Curve2D {
707 /// Constructs a centripetal Catmull-Rom spline curve.
708 ///
709 /// The `controlPoints` argument is a list of four or more points that
710 /// describe the points that the curve must pass through.
711 ///
712 /// The optional `tension` argument controls how tightly the curve approaches
713 /// the given `controlPoints`. It must be in the range 0.0 to 1.0, inclusive. It
714 /// defaults to 0.0, which provides the smoothest curve. A value of 1.0
715 /// produces a linear interpolation between points.
716 ///
717 /// The optional `endHandle` and `startHandle` points are the beginning and
718 /// ending handle positions. If not specified, they are created automatically
719 /// by extending the line formed by the first and/or last line segment in the
720 /// `controlPoints`, respectively. The spline will not go through these handle
721 /// points, but they will affect the slope of the line at the beginning and
722 /// end of the spline. The spline will attempt to match the slope of the line
723 /// formed by the start or end handle and the neighboring first or last
724 /// control point. The default is chosen so that the slope of the line at the
725 /// ends matches that of the first or last line segment in the control points.
726 ///
727 /// The `controlPoints` list must contain at least four control points to
728 /// interpolate.
729 ///
730 /// The internal curve data structures are lazily computed the first time
731 /// [transform] is called. If you would rather pre-compute the structures,
732 /// use [CatmullRomSpline.precompute] instead.
733 CatmullRomSpline(
734 List<Offset> controlPoints, {
735 double tension = 0.0,
736 Offset? startHandle,
737 Offset? endHandle,
738 }) : assert(tension <= 1.0, 'tension $tension must not be greater than 1.0.'),
739 assert(tension >= 0.0, 'tension $tension must not be negative.'),
740 assert(
741 controlPoints.length > 3,
742 'There must be at least four control points to create a CatmullRomSpline.',
743 ),
744 _controlPoints = controlPoints,
745 _startHandle = startHandle,
746 _endHandle = endHandle,
747 _tension = tension,
748 _cubicSegments = <List<Offset>>[];
749
750 /// Constructs a centripetal Catmull-Rom spline curve.
751 ///
752 /// The same as [CatmullRomSpline.new], except that the internal data
753 /// structures are precomputed instead of being computed lazily.
754 CatmullRomSpline.precompute(
755 List<Offset> controlPoints, {
756 double tension = 0.0,
757 Offset? startHandle,
758 Offset? endHandle,
759 }) : assert(tension <= 1.0, 'tension $tension must not be greater than 1.0.'),
760 assert(tension >= 0.0, 'tension $tension must not be negative.'),
761 assert(
762 controlPoints.length > 3,
763 'There must be at least four control points to create a CatmullRomSpline.',
764 ),
765 _controlPoints = null,
766 _startHandle = null,
767 _endHandle = null,
768 _tension = null,
769 _cubicSegments = _computeSegments(
770 controlPoints,
771 tension,
772 startHandle: startHandle,
773 endHandle: endHandle,
774 );
775
776 static List<List<Offset>> _computeSegments(
777 List<Offset> controlPoints,
778 double tension, {
779 Offset? startHandle,
780 Offset? endHandle,
781 }) {
782 assert(
783 startHandle == null || startHandle.isFinite,
784 'The provided startHandle of CatmullRomSpline must be finite. The '
785 'startHandle given was $startHandle.',
786 );
787 assert(
788 endHandle == null || endHandle.isFinite,
789 'The provided endHandle of CatmullRomSpline must be finite. The endHandle '
790 'given was $endHandle.',
791 );
792 assert(() {
793 for (int index = 0; index < controlPoints.length; index++) {
794 if (!controlPoints[index].isFinite) {
795 throw FlutterError(
796 'The provided CatmullRomSpline control point at index $index is not '
797 'finite. The control point given was ${controlPoints[index]}.',
798 );
799 }
800 }
801 return true;
802 }());
803 // If not specified, select the first and last control points (which are
804 // handles: they are not intersected by the resulting curve) so that they
805 // extend the first and last segments, respectively.
806 startHandle ??= controlPoints[0] * 2.0 - controlPoints[1];
807 endHandle ??= controlPoints.last * 2.0 - controlPoints[controlPoints.length - 2];
808 final List<Offset> allPoints = <Offset>[startHandle, ...controlPoints, endHandle];
809
810 // An alpha of 0.5 is what makes it a centripetal Catmull-Rom spline. A
811 // value of 0.0 would make it a uniform Catmull-Rom spline, and a value of
812 // 1.0 would make it a chordal Catmull-Rom spline. Non-centripetal values
813 // for alpha can give self-intersecting behavior or looping within a
814 // segment.
815 const double alpha = 0.5;
816 final double reverseTension = 1.0 - tension;
817 final List<List<Offset>> result = <List<Offset>>[];
818 for (int i = 0; i < allPoints.length - 3; ++i) {
819 final List<Offset> curve = <Offset>[
820 allPoints[i],
821 allPoints[i + 1],
822 allPoints[i + 2],
823 allPoints[i + 3],
824 ];
825 final Offset diffCurve10 = curve[1] - curve[0];
826 final Offset diffCurve21 = curve[2] - curve[1];
827 final Offset diffCurve32 = curve[3] - curve[2];
828 final double t01 = math.pow(diffCurve10.distance, alpha).toDouble();
829 final double t12 = math.pow(diffCurve21.distance, alpha).toDouble();
830 final double t23 = math.pow(diffCurve32.distance, alpha).toDouble();
831
832 final Offset m1 =
833 (diffCurve21 + (diffCurve10 / t01 - (curve[2] - curve[0]) / (t01 + t12)) * t12) *
834 reverseTension;
835 final Offset m2 =
836 (diffCurve21 + (diffCurve32 / t23 - (curve[3] - curve[1]) / (t12 + t23)) * t12) *
837 reverseTension;
838 final Offset sumM12 = m1 + m2;
839
840 final List<Offset> segment = <Offset>[
841 diffCurve21 * -2.0 + sumM12,
842 diffCurve21 * 3.0 - m1 - sumM12,
843 m1,
844 curve[1],
845 ];
846 result.add(segment);
847 }
848 return result;
849 }
850
851 // The list of control point lists for each cubic segment of the spline.
852 final List<List<Offset>> _cubicSegments;
853
854 // This is non-empty only if the _cubicSegments are being computed lazily.
855 final List<Offset>? _controlPoints;
856 final Offset? _startHandle;
857 final Offset? _endHandle;
858 final double? _tension;
859
860 void _initializeIfNeeded() {
861 if (_cubicSegments.isNotEmpty) {
862 return;
863 }
864 _cubicSegments.addAll(
865 _computeSegments(
866 _controlPoints!,
867 _tension!,
868 startHandle: _startHandle,
869 endHandle: _endHandle,
870 ),
871 );
872 }
873
874 @override
875 @protected
876 int get samplingSeed {
877 _initializeIfNeeded();
878 final Offset seedPoint = _cubicSegments[0][1];
879 return ((seedPoint.dx + seedPoint.dy) * 10000).round();
880 }
881
882 @override
883 Offset transformInternal(double t) {
884 _initializeIfNeeded();
885 final double length = _cubicSegments.length.toDouble();
886 final double position;
887 final double localT;
888 final int index;
889 if (t < 1.0) {
890 position = t * length;
891 localT = position % 1.0;
892 index = position.floor();
893 } else {
894 position = length;
895 localT = 1.0;
896 index = _cubicSegments.length - 1;
897 }
898 final List<Offset> cubicControlPoints = _cubicSegments[index];
899 final double localT2 = localT * localT;
900 return cubicControlPoints[0] * localT2 * localT +
901 cubicControlPoints[1] * localT2 +
902 cubicControlPoints[2] * localT +
903 cubicControlPoints[3];
904 }
905}
906
907/// An animation easing curve that passes smoothly through the given control
908/// points using a centripetal Catmull-Rom spline.
909///
910/// When this curve is evaluated with [transform], the values will interpolate
911/// smoothly from one control point to the next, passing through (0.0, 0.0), the
912/// given points, and then (1.0, 1.0).
913///
914/// {@macro flutter.animation.CatmullRomSpline}
915///
916/// This class uses a centripetal Catmull-Rom curve (a [CatmullRomSpline]) as
917/// its internal representation. The term centripetal implies that it won't form
918/// loops or self-intersections within a single segment, and corresponds to a
919/// Catmull-Rom α (alpha) value of 0.5.
920///
921/// See also:
922///
923/// * [CatmullRomSpline], the 2D spline that this curve uses to generate its values.
924/// * A Wikipedia article on [centripetal Catmull-Rom splines](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
925/// * [CatmullRomCurve.new] for a description of the constraints put on the
926/// input control points.
927/// * This [paper on using Catmull-Rom splines](http://faculty.cs.tamu.edu/schaefer/research/cr_cad.pdf).
928class CatmullRomCurve extends Curve {
929 /// Constructs a centripetal [CatmullRomCurve].
930 ///
931 /// It takes a list of two or more points that describe the points that the
932 /// curve must pass through. See [controlPoints] for a description of the
933 /// restrictions placed on control points. In addition to the given
934 /// [controlPoints], the curve will begin with an implicit control point at
935 /// (0.0, 0.0) and end with an implicit control point at (1.0, 1.0), so that
936 /// the curve begins and ends at those points.
937 ///
938 /// The optional [tension] argument controls how tightly the curve approaches
939 /// the given `controlPoints`. It must be in the range 0.0 to 1.0, inclusive. It
940 /// defaults to 0.0, which provides the smoothest curve. A value of 1.0
941 /// is equivalent to a linear interpolation between points.
942 ///
943 /// The internal curve data structures are lazily computed the first time
944 /// [transform] is called. If you would rather pre-compute the curve, use
945 /// [CatmullRomCurve.precompute] instead.
946 ///
947 /// See also:
948 ///
949 /// * This [paper on using Catmull-Rom splines](http://faculty.cs.tamu.edu/schaefer/research/cr_cad.pdf).
950 CatmullRomCurve(this.controlPoints, {this.tension = 0.0})
951 : assert(
952 () {
953 return validateControlPoints(
954 controlPoints,
955 tension: tension,
956 reasons: _debugAssertReasons..clear(),
957 );
958 }(),
959 'control points $controlPoints could not be validated:\n ${_debugAssertReasons.join('\n ')}',
960 ),
961 // Pre-compute samples so that we don't have to evaluate the spline's inverse
962 // all the time in transformInternal.
963 _precomputedSamples = <Curve2DSample>[];
964
965 /// Constructs a centripetal [CatmullRomCurve].
966 ///
967 /// Same as [CatmullRomCurve.new], but it precomputes the internal curve data
968 /// structures for a more predictable computation load.
969 CatmullRomCurve.precompute(this.controlPoints, {this.tension = 0.0})
970 : assert(
971 () {
972 return validateControlPoints(
973 controlPoints,
974 tension: tension,
975 reasons: _debugAssertReasons..clear(),
976 );
977 }(),
978 'control points $controlPoints could not be validated:\n ${_debugAssertReasons.join('\n ')}',
979 ),
980 // Pre-compute samples so that we don't have to evaluate the spline's inverse
981 // all the time in transformInternal.
982 _precomputedSamples = _computeSamples(controlPoints, tension);
983
984 static List<Curve2DSample> _computeSamples(List<Offset> controlPoints, double tension) {
985 return CatmullRomSpline.precompute(
986 // Force the first and last control points for the spline to be (0, 0)
987 // and (1, 1), respectively.
988 <Offset>[Offset.zero, ...controlPoints, const Offset(1.0, 1.0)],
989 tension: tension,
990 ).generateSamples(tolerance: 1e-12).toList();
991 }
992
993 /// A static accumulator for assertion failures. Not used in release mode.
994 static final List<String> _debugAssertReasons = <String>[];
995
996 // The precomputed approximation curve, so that evaluation of the curve is
997 // efficient.
998 //
999 // If the curve is constructed lazily, then this will be empty, and will be filled
1000 // the first time transform is called.
1001 final List<Curve2DSample> _precomputedSamples;
1002
1003 /// The control points used to create this curve.
1004 ///
1005 /// The `dx` value of each [Offset] in [controlPoints] represents the
1006 /// animation value at which the curve should pass through the `dy` value of
1007 /// the same control point.
1008 ///
1009 /// The [controlPoints] list must meet the following criteria:
1010 ///
1011 /// * The list must contain at least two points.
1012 /// * The X value of each point must be greater than 0.0 and less then 1.0.
1013 /// * The X values of each point must be greater than the
1014 /// previous point's X value (i.e. monotonically increasing). The Y values
1015 /// are not constrained.
1016 /// * The resulting spline must be single-valued in X. That is, for each X
1017 /// value, there must be exactly one Y value. This means that the control
1018 /// points must not generated a spline that loops or overlaps itself.
1019 ///
1020 /// The static function [validateControlPoints] can be used to check that
1021 /// these conditions are met, and will return true if they are. In debug mode,
1022 /// it will also optionally return a list of reasons in text form. In debug
1023 /// mode, the constructor will assert that these conditions are met and print
1024 /// the reasons if the assert fires.
1025 ///
1026 /// When the curve is evaluated with [transform], the values will interpolate
1027 /// smoothly from one control point to the next, passing through (0.0, 0.0), the
1028 /// given control points, and (1.0, 1.0).
1029 final List<Offset> controlPoints;
1030
1031 /// The "tension" of the curve.
1032 ///
1033 /// The [tension] attribute controls how tightly the curve approaches the
1034 /// given [controlPoints]. It must be in the range 0.0 to 1.0, inclusive. It
1035 /// is optional, and defaults to 0.0, which provides the smoothest curve. A
1036 /// value of 1.0 is equivalent to a linear interpolation between control
1037 /// points.
1038 final double tension;
1039
1040 /// Validates that a given set of control points for a [CatmullRomCurve] is
1041 /// well-formed and will not produce a spline that self-intersects.
1042 ///
1043 /// This method is also used in debug mode to validate a curve to make sure
1044 /// that it won't violate the contract for the [CatmullRomCurve.new]
1045 /// constructor.
1046 ///
1047 /// If in debug mode, and `reasons` is non-null, this function will fill in
1048 /// `reasons` with descriptions of the problems encountered. The `reasons`
1049 /// argument is ignored in release mode.
1050 ///
1051 /// In release mode, this function can be used to decide if a proposed
1052 /// modification to the curve will result in a valid curve.
1053 static bool validateControlPoints(
1054 List<Offset>? controlPoints, {
1055 double tension = 0.0,
1056 List<String>? reasons,
1057 }) {
1058 if (controlPoints == null) {
1059 assert(() {
1060 reasons?.add('Supplied control points cannot be null');
1061 return true;
1062 }());
1063 return false;
1064 }
1065
1066 if (controlPoints.length < 2) {
1067 assert(() {
1068 reasons?.add('There must be at least two points supplied to create a valid curve.');
1069 return true;
1070 }());
1071 return false;
1072 }
1073
1074 controlPoints = <Offset>[Offset.zero, ...controlPoints, const Offset(1.0, 1.0)];
1075 final Offset startHandle = controlPoints[0] * 2.0 - controlPoints[1];
1076 final Offset endHandle = controlPoints.last * 2.0 - controlPoints[controlPoints.length - 2];
1077 controlPoints = <Offset>[startHandle, ...controlPoints, endHandle];
1078 double lastX = -double.infinity;
1079 for (int i = 0; i < controlPoints.length; ++i) {
1080 if (i > 1 &&
1081 i < controlPoints.length - 2 &&
1082 (controlPoints[i].dx <= 0.0 || controlPoints[i].dx >= 1.0)) {
1083 assert(() {
1084 reasons?.add(
1085 'Control points must have X values between 0.0 and 1.0, exclusive. '
1086 'Point $i has an x value (${controlPoints![i].dx}) which is outside the range.',
1087 );
1088 return true;
1089 }());
1090 return false;
1091 }
1092 if (controlPoints[i].dx <= lastX) {
1093 assert(() {
1094 reasons?.add(
1095 'Each X coordinate must be greater than the preceding X coordinate '
1096 '(i.e. must be monotonically increasing in X). Point $i has an x value of '
1097 '${controlPoints![i].dx}, which is not greater than $lastX',
1098 );
1099 return true;
1100 }());
1101 return false;
1102 }
1103 lastX = controlPoints[i].dx;
1104 }
1105
1106 bool success = true;
1107
1108 // An empirical test to make sure things are single-valued in X.
1109 lastX = -double.infinity;
1110 const double tolerance = 1e-3;
1111 final CatmullRomSpline testSpline = CatmullRomSpline(controlPoints, tension: tension);
1112 final double start = testSpline.findInverse(0.0);
1113 final double end = testSpline.findInverse(1.0);
1114 final Iterable<Curve2DSample> samplePoints = testSpline.generateSamples(start: start, end: end);
1115
1116 /// If the first and last points in the samples aren't at (0,0) or (1,1)
1117 /// respectively, then the curve is multi-valued at the ends.
1118 if (samplePoints.first.value.dy.abs() > tolerance ||
1119 (1.0 - samplePoints.last.value.dy).abs() > tolerance) {
1120 bool bail = true;
1121 success = false;
1122 assert(() {
1123 reasons?.add(
1124 'The curve has more than one Y value at X = ${samplePoints.first.value.dx}. '
1125 'Try moving some control points further away from this value of X, or increasing '
1126 'the tension.',
1127 );
1128 // No need to keep going if we're not giving reasons.
1129 bail = reasons == null;
1130 return true;
1131 }());
1132 if (bail) {
1133 // If we're not in debug mode, then we want to bail immediately
1134 // instead of checking everything else.
1135 return false;
1136 }
1137 }
1138 for (final Curve2DSample sample in samplePoints) {
1139 final Offset point = sample.value;
1140 final double t = sample.t;
1141 final double x = point.dx;
1142 if (t >= start && t <= end && (x < -1e-3 || x > 1.0 + 1e-3)) {
1143 bool bail = true;
1144 success = false;
1145 assert(() {
1146 reasons?.add(
1147 'The resulting curve has an X value ($x) which is outside '
1148 'the range [0.0, 1.0], inclusive.',
1149 );
1150 // No need to keep going if we're not giving reasons.
1151 bail = reasons == null;
1152 return true;
1153 }());
1154 if (bail) {
1155 // If we're not in debug mode, then we want to bail immediately
1156 // instead of checking all the segments.
1157 return false;
1158 }
1159 }
1160 if (x < lastX) {
1161 bool bail = true;
1162 success = false;
1163 assert(() {
1164 reasons?.add(
1165 'The curve has more than one Y value at x = $x. Try moving '
1166 'some control points further apart in X, or increasing the tension.',
1167 );
1168 // No need to keep going if we're not giving reasons.
1169 bail = reasons == null;
1170 return true;
1171 }());
1172 if (bail) {
1173 // If we're not in debug mode, then we want to bail immediately
1174 // instead of checking all the segments.
1175 return false;
1176 }
1177 }
1178 lastX = x;
1179 }
1180 return success;
1181 }
1182
1183 @override
1184 double transformInternal(double t) {
1185 // Linearly interpolate between the two closest samples generated when the
1186 // curve was created.
1187 if (_precomputedSamples.isEmpty) {
1188 // Compute the samples now if we were constructed lazily.
1189 _precomputedSamples.addAll(_computeSamples(controlPoints, tension));
1190 }
1191 int start = 0;
1192 int end = _precomputedSamples.length - 1;
1193 int mid;
1194 Offset value;
1195 Offset startValue = _precomputedSamples[start].value;
1196 Offset endValue = _precomputedSamples[end].value;
1197 // Use a binary search to find the index of the sample point that is just
1198 // before t.
1199 while (end - start > 1) {
1200 mid = (end + start) ~/ 2;
1201 value = _precomputedSamples[mid].value;
1202 if (t >= value.dx) {
1203 start = mid;
1204 startValue = value;
1205 } else {
1206 end = mid;
1207 endValue = value;
1208 }
1209 }
1210
1211 // Now interpolate between the found sample and the next one.
1212 final double t2 = (t - startValue.dx) / (endValue.dx - startValue.dx);
1213 return lerpDouble(startValue.dy, endValue.dy, t2)!;
1214 }
1215}
1216
1217/// A curve that is the reversed inversion of its given curve.
1218///
1219/// This curve evaluates the given curve in reverse (i.e., from 1.0 to 0.0 as t
1220/// increases from 0.0 to 1.0) and returns the inverse of the given curve's
1221/// value (i.e., 1.0 minus the given curve's value).
1222///
1223/// This is the class used to implement the [flipped] getter on curves.
1224///
1225/// This is often useful with [CurvedAnimation.reverseCurve].
1226///
1227/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
1228/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_flipped.mp4}
1229///
1230/// See also:
1231///
1232/// * [Curve.flipped], which provides the [FlippedCurve] of a [Curve].
1233/// * [ReverseAnimation], which reverses an [Animation] rather than a [Curve].
1234/// * [CurvedAnimation], which can take a separate curve and reverse curve.
1235class FlippedCurve extends Curve {
1236 /// Creates a flipped curve.
1237 const FlippedCurve(this.curve);
1238
1239 /// The curve that is being flipped.
1240 final Curve curve;
1241
1242 @override
1243 double transformInternal(double t) => 1.0 - curve.transform(1.0 - t);
1244
1245 @override
1246 String toString() {
1247 return '${objectRuntimeType(this, 'FlippedCurve')}($curve)';
1248 }
1249}
1250
1251/// A curve where the rate of change starts out quickly and then decelerates; an
1252/// upside-down `f(t) = t²` parabola.
1253///
1254/// This is equivalent to the Android `DecelerateInterpolator` class with a unit
1255/// factor (the default factor).
1256///
1257/// See [Curves.decelerate] for an instance of this class.
1258class _DecelerateCurve extends Curve {
1259 const _DecelerateCurve._();
1260
1261 @override
1262 double transformInternal(double t) {
1263 // Intended to match the behavior of:
1264 // https://android.googlesource.com/platform/frameworks/base/+/main/core/java/android/view/animation/DecelerateInterpolator.java
1265 // ...as of December 2016.
1266 t = 1.0 - t;
1267 return 1.0 - t * t;
1268 }
1269}
1270
1271// BOUNCE CURVES
1272
1273double _bounce(double t) {
1274 if (t < 1.0 / 2.75) {
1275 return 7.5625 * t * t;
1276 } else if (t < 2 / 2.75) {
1277 t -= 1.5 / 2.75;
1278 return 7.5625 * t * t + 0.75;
1279 } else if (t < 2.5 / 2.75) {
1280 t -= 2.25 / 2.75;
1281 return 7.5625 * t * t + 0.9375;
1282 }
1283 t -= 2.625 / 2.75;
1284 return 7.5625 * t * t + 0.984375;
1285}
1286
1287/// An oscillating curve that grows in magnitude.
1288///
1289/// See [Curves.bounceIn] for an instance of this class.
1290class _BounceInCurve extends Curve {
1291 const _BounceInCurve._();
1292
1293 @override
1294 double transformInternal(double t) {
1295 return 1.0 - _bounce(1.0 - t);
1296 }
1297}
1298
1299/// An oscillating curve that shrink in magnitude.
1300///
1301/// See [Curves.bounceOut] for an instance of this class.
1302class _BounceOutCurve extends Curve {
1303 const _BounceOutCurve._();
1304
1305 @override
1306 double transformInternal(double t) {
1307 return _bounce(t);
1308 }
1309}
1310
1311/// An oscillating curve that first grows and then shrink in magnitude.
1312///
1313/// See [Curves.bounceInOut] for an instance of this class.
1314class _BounceInOutCurve extends Curve {
1315 const _BounceInOutCurve._();
1316
1317 @override
1318 double transformInternal(double t) {
1319 if (t < 0.5) {
1320 return (1.0 - _bounce(1.0 - t * 2.0)) * 0.5;
1321 } else {
1322 return _bounce(t * 2.0 - 1.0) * 0.5 + 0.5;
1323 }
1324 }
1325}
1326
1327// ELASTIC CURVES
1328
1329/// An oscillating curve that grows in magnitude while overshooting its bounds.
1330///
1331/// An instance of this class using the default period of 0.4 is available as
1332/// [Curves.elasticIn].
1333///
1334/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in.mp4}
1335class ElasticInCurve extends Curve {
1336 /// Creates an elastic-in curve.
1337 ///
1338 /// Rather than creating a new instance, consider using [Curves.elasticIn].
1339 const ElasticInCurve([this.period = 0.4]);
1340
1341 /// The duration of the oscillation.
1342 final double period;
1343
1344 @override
1345 double transformInternal(double t) {
1346 final double s = period / 4.0;
1347 t = t - 1.0;
1348 return -math.pow(2.0, 10.0 * t) * math.sin((t - s) * (math.pi * 2.0) / period);
1349 }
1350
1351 @override
1352 String toString() {
1353 return '${objectRuntimeType(this, 'ElasticInCurve')}($period)';
1354 }
1355}
1356
1357/// An oscillating curve that shrinks in magnitude while overshooting its bounds.
1358///
1359/// An instance of this class using the default period of 0.4 is available as
1360/// [Curves.elasticOut].
1361///
1362/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_out.mp4}
1363class ElasticOutCurve extends Curve {
1364 /// Creates an elastic-out curve.
1365 ///
1366 /// Rather than creating a new instance, consider using [Curves.elasticOut].
1367 const ElasticOutCurve([this.period = 0.4]);
1368
1369 /// The duration of the oscillation.
1370 final double period;
1371
1372 @override
1373 double transformInternal(double t) {
1374 final double s = period / 4.0;
1375 return math.pow(2.0, -10 * t) * math.sin((t - s) * (math.pi * 2.0) / period) + 1.0;
1376 }
1377
1378 @override
1379 String toString() {
1380 return '${objectRuntimeType(this, 'ElasticOutCurve')}($period)';
1381 }
1382}
1383
1384/// An oscillating curve that grows and then shrinks in magnitude while
1385/// overshooting its bounds.
1386///
1387/// An instance of this class using the default period of 0.4 is available as
1388/// [Curves.elasticInOut].
1389///
1390/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in_out.mp4}
1391class ElasticInOutCurve extends Curve {
1392 /// Creates an elastic-in-out curve.
1393 ///
1394 /// Rather than creating a new instance, consider using [Curves.elasticInOut].
1395 const ElasticInOutCurve([this.period = 0.4]);
1396
1397 /// The duration of the oscillation.
1398 final double period;
1399
1400 @override
1401 double transformInternal(double t) {
1402 final double s = period / 4.0;
1403 t = 2.0 * t - 1.0;
1404 if (t < 0.0) {
1405 return -0.5 * math.pow(2.0, 10.0 * t) * math.sin((t - s) * (math.pi * 2.0) / period);
1406 } else {
1407 return math.pow(2.0, -10.0 * t) * math.sin((t - s) * (math.pi * 2.0) / period) * 0.5 + 1.0;
1408 }
1409 }
1410
1411 @override
1412 String toString() {
1413 return '${objectRuntimeType(this, 'ElasticInOutCurve')}($period)';
1414 }
1415}
1416
1417// PREDEFINED CURVES
1418
1419/// A collection of common animation curves.
1420///
1421/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
1422/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in_out.mp4}
1423/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_out.mp4}
1424/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_decelerate.mp4}
1425/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
1426/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4}
1427/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_sine.mp4}
1428/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quad.mp4}
1429/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_cubic.mp4}
1430/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quart.mp4}
1431/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quint.mp4}
1432/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_expo.mp4}
1433/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_circ.mp4}
1434/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_back.mp4}
1435/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out.mp4}
1436/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_sine.mp4}
1437/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quad.mp4}
1438/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic.mp4}
1439/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quart.mp4}
1440/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quint.mp4}
1441/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_expo.mp4}
1442/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_circ.mp4}
1443/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_back.mp4}
1444/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4}
1445/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_sine.mp4}
1446/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quad.mp4}
1447/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_cubic.mp4}
1448/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quart.mp4}
1449/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quint.mp4}
1450/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_expo.mp4}
1451/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_circ.mp4}
1452/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_back.mp4}
1453/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in.mp4}
1454/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in_out.mp4}
1455/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_out.mp4}
1456/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_out_slow_in.mp4}
1457/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_slow_middle.mp4}
1458/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4}
1459///
1460/// See also:
1461///
1462/// * [Curve], the interface implemented by the constants available from the
1463/// [Curves] class.
1464/// * [Easing], for the Material animation curves.
1465abstract final class Curves {
1466 /// A linear animation curve.
1467 ///
1468 /// This is the identity map over the unit interval: its [Curve.transform]
1469 /// method returns its input unmodified. This is useful as a default curve for
1470 /// cases where a [Curve] is required but no actual curve is desired.
1471 ///
1472 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear.mp4}
1473 static const Curve linear = _Linear._();
1474
1475 /// A curve where the rate of change starts out quickly and then decelerates; an
1476 /// upside-down `f(t) = t²` parabola.
1477 ///
1478 /// This is equivalent to the Android `DecelerateInterpolator` class with a unit
1479 /// factor (the default factor).
1480 ///
1481 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_decelerate.mp4}
1482 static const Curve decelerate = _DecelerateCurve._();
1483
1484 /// A curve that is very steep and linear at the beginning, but quickly flattens out
1485 /// and very slowly eases in.
1486 ///
1487 /// By default is the curve used to animate pages on iOS back to their original
1488 /// position if a swipe gesture is ended midway through a swipe.
1489 ///
1490 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_linear_to_slow_ease_in.mp4}
1491 static const Cubic fastLinearToSlowEaseIn = Cubic(0.18, 1.0, 0.04, 1.0);
1492
1493 /// A curve that starts slowly, speeds up very quickly, and then ends slowly.
1494 ///
1495 /// This curve is used by default to animate page transitions used by
1496 /// [CupertinoPageRoute].
1497 ///
1498 /// It has been derived from plots of native iOS 16.3
1499 /// animation frames on iPhone 14 Pro Max.
1500 /// Specifically, transition animation positions were measured
1501 /// every frame and plotted against time. Then, a cubic curve was
1502 /// strictly fit to the measured data points.
1503 ///
1504 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_ease_in_to_slow_ease_out.mp4}
1505 static const ThreePointCubic fastEaseInToSlowEaseOut = ThreePointCubic(
1506 Offset(0.056, 0.024),
1507 Offset(0.108, 0.3085),
1508 Offset(0.198, 0.541),
1509 Offset(0.3655, 1.0),
1510 Offset(0.5465, 0.989),
1511 );
1512
1513 /// A cubic animation curve that speeds up quickly and ends slowly.
1514 ///
1515 /// This is the same as the CSS easing function `ease`.
1516 ///
1517 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
1518 static const Cubic ease = Cubic(0.25, 0.1, 0.25, 1.0);
1519
1520 /// A cubic animation curve that starts slowly and ends quickly.
1521 ///
1522 /// This is the same as the CSS easing function `ease-in`.
1523 ///
1524 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4}
1525 static const Cubic easeIn = Cubic(0.42, 0.0, 1.0, 1.0);
1526
1527 /// A cubic animation curve that starts slowly and ends linearly.
1528 ///
1529 /// The symmetric animation to [linearToEaseOut].
1530 ///
1531 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_to_linear.mp4}
1532 static const Cubic easeInToLinear = Cubic(0.67, 0.03, 0.65, 0.09);
1533
1534 /// A cubic animation curve that starts slowly and ends quickly. This is
1535 /// similar to [Curves.easeIn], but with sinusoidal easing for a slightly less
1536 /// abrupt beginning and end. Nonetheless, the result is quite gentle and is
1537 /// hard to distinguish from [Curves.linear] at a glance.
1538 ///
1539 /// Derived from Robert Penner’s easing functions.
1540 ///
1541 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_sine.mp4}
1542 static const Cubic easeInSine = Cubic(0.47, 0.0, 0.745, 0.715);
1543
1544 /// A cubic animation curve that starts slowly and ends quickly. Based on a
1545 /// quadratic equation where `f(t) = t²`, this is effectively the inverse of
1546 /// [Curves.decelerate].
1547 ///
1548 /// Compared to [Curves.easeInSine], this curve is slightly steeper.
1549 ///
1550 /// Derived from Robert Penner’s easing functions.
1551 ///
1552 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quad.mp4}
1553 static const Cubic easeInQuad = Cubic(0.55, 0.085, 0.68, 0.53);
1554
1555 /// A cubic animation curve that starts slowly and ends quickly. This curve is
1556 /// based on a cubic equation where `f(t) = t³`. The result is a safe sweet
1557 /// spot when choosing a curve for widgets animating off the viewport.
1558 ///
1559 /// Compared to [Curves.easeInQuad], this curve is slightly steeper.
1560 ///
1561 /// Derived from Robert Penner’s easing functions.
1562 ///
1563 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_cubic.mp4}
1564 static const Cubic easeInCubic = Cubic(0.55, 0.055, 0.675, 0.19);
1565
1566 /// A cubic animation curve that starts slowly and ends quickly. This curve is
1567 /// based on a quartic equation where `f(t) = t⁴`.
1568 ///
1569 /// Animations using this curve or steeper curves will benefit from a longer
1570 /// duration to avoid motion feeling unnatural.
1571 ///
1572 /// Compared to [Curves.easeInCubic], this curve is slightly steeper.
1573 ///
1574 /// Derived from Robert Penner’s easing functions.
1575 ///
1576 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quart.mp4}
1577 static const Cubic easeInQuart = Cubic(0.895, 0.03, 0.685, 0.22);
1578
1579 /// A cubic animation curve that starts slowly and ends quickly. This curve is
1580 /// based on a quintic equation where `f(t) = t⁵`.
1581 ///
1582 /// Compared to [Curves.easeInQuart], this curve is slightly steeper.
1583 ///
1584 /// Derived from Robert Penner’s easing functions.
1585 ///
1586 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_quint.mp4}
1587 static const Cubic easeInQuint = Cubic(0.755, 0.05, 0.855, 0.06);
1588
1589 /// A cubic animation curve that starts slowly and ends quickly. This curve is
1590 /// based on an exponential equation where `f(t) = 2¹⁰⁽ᵗ⁻¹⁾`.
1591 ///
1592 /// Using this curve can give your animations extra flare, but a longer
1593 /// duration may need to be used to compensate for the steepness of the curve.
1594 ///
1595 /// Compared to [Curves.easeInQuint], this curve is slightly steeper.
1596 ///
1597 /// Derived from Robert Penner’s easing functions.
1598 ///
1599 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_expo.mp4}
1600 static const Cubic easeInExpo = Cubic(0.95, 0.05, 0.795, 0.035);
1601
1602 /// A cubic animation curve that starts slowly and ends quickly. This curve is
1603 /// effectively the bottom-right quarter of a circle.
1604 ///
1605 /// Like [Curves.easeInExpo], this curve is fairly dramatic and will reduce
1606 /// the clarity of an animation if not given a longer duration.
1607 ///
1608 /// Derived from Robert Penner’s easing functions.
1609 ///
1610 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_circ.mp4}
1611 static const Cubic easeInCirc = Cubic(0.6, 0.04, 0.98, 0.335);
1612
1613 /// A cubic animation curve that starts slowly and ends quickly. This curve
1614 /// is similar to [Curves.elasticIn] in that it overshoots its bounds before
1615 /// reaching its end. Instead of repeated swinging motions before ascending,
1616 /// though, this curve overshoots once, then continues to ascend.
1617 ///
1618 /// Derived from Robert Penner’s easing functions.
1619 ///
1620 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_back.mp4}
1621 static const Cubic easeInBack = Cubic(0.6, -0.28, 0.735, 0.045);
1622
1623 /// A cubic animation curve that starts quickly and ends slowly.
1624 ///
1625 /// This is the same as the CSS easing function `ease-out`.
1626 ///
1627 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4}
1628 static const Cubic easeOut = Cubic(0.0, 0.0, 0.58, 1.0);
1629
1630 /// A cubic animation curve that starts linearly and ends slowly.
1631 ///
1632 /// A symmetric animation to [easeInToLinear].
1633 ///
1634 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_linear_to_ease_out.mp4}
1635 static const Cubic linearToEaseOut = Cubic(0.35, 0.91, 0.33, 0.97);
1636
1637 /// A cubic animation curve that starts quickly and ends slowly. This is
1638 /// similar to [Curves.easeOut], but with sinusoidal easing for a slightly
1639 /// less abrupt beginning and end. Nonetheless, the result is quite gentle and
1640 /// is hard to distinguish from [Curves.linear] at a glance.
1641 ///
1642 /// Derived from Robert Penner’s easing functions.
1643 ///
1644 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_sine.mp4}
1645 static const Cubic easeOutSine = Cubic(0.39, 0.575, 0.565, 1.0);
1646
1647 /// A cubic animation curve that starts quickly and ends slowly. This is
1648 /// effectively the same as [Curves.decelerate], only simulated using a cubic
1649 /// bezier function.
1650 ///
1651 /// Compared to [Curves.easeOutSine], this curve is slightly steeper.
1652 ///
1653 /// Derived from Robert Penner’s easing functions.
1654 ///
1655 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quad.mp4}
1656 static const Cubic easeOutQuad = Cubic(0.25, 0.46, 0.45, 0.94);
1657
1658 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1659 /// a flipped version of [Curves.easeInCubic].
1660 ///
1661 /// The result is a safe sweet spot when choosing a curve for animating a
1662 /// widget's position entering or already inside the viewport.
1663 ///
1664 /// Compared to [Curves.easeOutQuad], this curve is slightly steeper.
1665 ///
1666 /// Derived from Robert Penner’s easing functions.
1667 ///
1668 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_cubic.mp4}
1669 static const Cubic easeOutCubic = Cubic(0.215, 0.61, 0.355, 1.0);
1670
1671 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1672 /// a flipped version of [Curves.easeInQuart].
1673 ///
1674 /// Animations using this curve or steeper curves will benefit from a longer
1675 /// duration to avoid motion feeling unnatural.
1676 ///
1677 /// Compared to [Curves.easeOutCubic], this curve is slightly steeper.
1678 ///
1679 /// Derived from Robert Penner’s easing functions.
1680 ///
1681 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quart.mp4}
1682 static const Cubic easeOutQuart = Cubic(0.165, 0.84, 0.44, 1.0);
1683
1684 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1685 /// a flipped version of [Curves.easeInQuint].
1686 ///
1687 /// Compared to [Curves.easeOutQuart], this curve is slightly steeper.
1688 ///
1689 /// Derived from Robert Penner’s easing functions.
1690 ///
1691 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_quint.mp4}
1692 static const Cubic easeOutQuint = Cubic(0.23, 1.0, 0.32, 1.0);
1693
1694 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1695 /// a flipped version of [Curves.easeInExpo]. Using this curve can give your
1696 /// animations extra flare, but a longer duration may need to be used to
1697 /// compensate for the steepness of the curve.
1698 ///
1699 /// Derived from Robert Penner’s easing functions.
1700 ///
1701 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_expo.mp4}
1702 static const Cubic easeOutExpo = Cubic(0.19, 1.0, 0.22, 1.0);
1703
1704 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1705 /// effectively the top-left quarter of a circle.
1706 ///
1707 /// Like [Curves.easeOutExpo], this curve is fairly dramatic and will reduce
1708 /// the clarity of an animation if not given a longer duration.
1709 ///
1710 /// Derived from Robert Penner’s easing functions.
1711 ///
1712 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_circ.mp4}
1713 static const Cubic easeOutCirc = Cubic(0.075, 0.82, 0.165, 1.0);
1714
1715 /// A cubic animation curve that starts quickly and ends slowly. This curve is
1716 /// similar to [Curves.elasticOut] in that it overshoots its bounds before
1717 /// reaching its end. Instead of repeated swinging motions after ascending,
1718 /// though, this curve only overshoots once.
1719 ///
1720 /// Derived from Robert Penner’s easing functions.
1721 ///
1722 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out_back.mp4}
1723 static const Cubic easeOutBack = Cubic(0.175, 0.885, 0.32, 1.275);
1724
1725 /// A cubic animation curve that starts slowly, speeds up, and then ends
1726 /// slowly.
1727 ///
1728 /// This is the same as the CSS easing function `ease-in-out`.
1729 ///
1730 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out.mp4}
1731 static const Cubic easeInOut = Cubic(0.42, 0.0, 0.58, 1.0);
1732
1733 /// A cubic animation curve that starts slowly, speeds up, and then ends
1734 /// slowly. This is similar to [Curves.easeInOut], but with sinusoidal easing
1735 /// for a slightly less abrupt beginning and end.
1736 ///
1737 /// Derived from Robert Penner’s easing functions.
1738 ///
1739 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_sine.mp4}
1740 static const Cubic easeInOutSine = Cubic(0.445, 0.05, 0.55, 0.95);
1741
1742 /// A cubic animation curve that starts slowly, speeds up, and then ends
1743 /// slowly. This curve can be imagined as [Curves.easeInQuad] as the first
1744 /// half, and [Curves.easeOutQuad] as the second.
1745 ///
1746 /// Compared to [Curves.easeInOutSine], this curve is slightly steeper.
1747 ///
1748 /// Derived from Robert Penner’s easing functions.
1749 ///
1750 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quad.mp4}
1751 static const Cubic easeInOutQuad = Cubic(0.455, 0.03, 0.515, 0.955);
1752
1753 /// A cubic animation curve that starts slowly, speeds up, and then ends
1754 /// slowly. This curve can be imagined as [Curves.easeInCubic] as the first
1755 /// half, and [Curves.easeOutCubic] as the second.
1756 ///
1757 /// The result is a safe sweet spot when choosing a curve for a widget whose
1758 /// initial and final positions are both within the viewport.
1759 ///
1760 /// Compared to [Curves.easeInOutQuad], this curve is slightly steeper.
1761 ///
1762 /// Derived from Robert Penner’s easing functions.
1763 ///
1764 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic.mp4}
1765 static const Cubic easeInOutCubic = Cubic(0.645, 0.045, 0.355, 1.0);
1766
1767 /// A cubic animation curve that starts slowly, speeds up shortly thereafter,
1768 /// and then ends slowly. This curve can be imagined as a steeper version of
1769 /// [easeInOutCubic].
1770 ///
1771 /// The result is a more emphasized eased curve when choosing a curve for a
1772 /// widget whose initial and final positions are both within the viewport.
1773 ///
1774 /// Compared to [Curves.easeInOutCubic], this curve is slightly steeper.
1775 ///
1776 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_cubic_emphasized.mp4}
1777 static const ThreePointCubic easeInOutCubicEmphasized = ThreePointCubic(
1778 Offset(0.05, 0),
1779 Offset(0.133333, 0.06),
1780 Offset(0.166666, 0.4),
1781 Offset(0.208333, 0.82),
1782 Offset(0.25, 1),
1783 );
1784
1785 /// A cubic animation curve that starts slowly, speeds up, and then ends
1786 /// slowly. This curve can be imagined as [Curves.easeInQuart] as the first
1787 /// half, and [Curves.easeOutQuart] as the second.
1788 ///
1789 /// Animations using this curve or steeper curves will benefit from a longer
1790 /// duration to avoid motion feeling unnatural.
1791 ///
1792 /// Compared to [Curves.easeInOutCubic], this curve is slightly steeper.
1793 ///
1794 /// Derived from Robert Penner’s easing functions.
1795 ///
1796 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quart.mp4}
1797 static const Cubic easeInOutQuart = Cubic(0.77, 0.0, 0.175, 1.0);
1798
1799 /// A cubic animation curve that starts slowly, speeds up, and then ends
1800 /// slowly. This curve can be imagined as [Curves.easeInQuint] as the first
1801 /// half, and [Curves.easeOutQuint] as the second.
1802 ///
1803 /// Compared to [Curves.easeInOutQuart], this curve is slightly steeper.
1804 ///
1805 /// Derived from Robert Penner’s easing functions.
1806 ///
1807 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_quint.mp4}
1808 static const Cubic easeInOutQuint = Cubic(0.86, 0.0, 0.07, 1.0);
1809
1810 /// A cubic animation curve that starts slowly, speeds up, and then ends
1811 /// slowly.
1812 ///
1813 /// Since this curve is arrived at with an exponential function, the midpoint
1814 /// is exceptionally steep. Extra consideration should be taken when designing
1815 /// an animation using this.
1816 ///
1817 /// Compared to [Curves.easeInOutQuint], this curve is slightly steeper.
1818 ///
1819 /// Derived from Robert Penner’s easing functions.
1820 ///
1821 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_expo.mp4}
1822 static const Cubic easeInOutExpo = Cubic(1.0, 0.0, 0.0, 1.0);
1823
1824 /// A cubic animation curve that starts slowly, speeds up, and then ends
1825 /// slowly. This curve can be imagined as [Curves.easeInCirc] as the first
1826 /// half, and [Curves.easeOutCirc] as the second.
1827 ///
1828 /// Like [Curves.easeInOutExpo], this curve is fairly dramatic and will reduce
1829 /// the clarity of an animation if not given a longer duration.
1830 ///
1831 /// Compared to [Curves.easeInOutExpo], this curve is slightly steeper.
1832 ///
1833 /// Derived from Robert Penner’s easing functions.
1834 ///
1835 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_circ.mp4}
1836 static const Cubic easeInOutCirc = Cubic(0.785, 0.135, 0.15, 0.86);
1837
1838 /// A cubic animation curve that starts slowly, speeds up, and then ends
1839 /// slowly. This curve can be imagined as [Curves.easeInBack] as the first
1840 /// half, and [Curves.easeOutBack] as the second.
1841 ///
1842 /// Since two curves are used as a basis for this curve, the resulting
1843 /// animation will overshoot its bounds twice before reaching its end - first
1844 /// by exceeding its lower bound, then exceeding its upper bound and finally
1845 /// descending to its final position.
1846 ///
1847 /// Derived from Robert Penner’s easing functions.
1848 ///
1849 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_out_back.mp4}
1850 static const Cubic easeInOutBack = Cubic(0.68, -0.55, 0.265, 1.55);
1851
1852 /// A curve that starts quickly and eases into its final position.
1853 ///
1854 /// Over the course of the animation, the object spends more time near its
1855 /// final destination. As a result, the user isn’t left waiting for the
1856 /// animation to finish, and the negative effects of motion are minimized.
1857 ///
1858 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_out_slow_in.mp4}
1859 ///
1860 /// See also:
1861 ///
1862 /// * [Easing.legacy], the name for this curve in the Material specification.
1863 static const Cubic fastOutSlowIn = Cubic(0.4, 0.0, 0.2, 1.0);
1864
1865 /// A cubic animation curve that starts quickly, slows down, and then ends
1866 /// quickly.
1867 ///
1868 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_slow_middle.mp4}
1869 static const Cubic slowMiddle = Cubic(0.15, 0.85, 0.85, 0.15);
1870
1871 /// An oscillating curve that grows in magnitude.
1872 ///
1873 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in.mp4}
1874 static const Curve bounceIn = _BounceInCurve._();
1875
1876 /// An oscillating curve that first grows and then shrink in magnitude.
1877 ///
1878 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_out.mp4}
1879 static const Curve bounceOut = _BounceOutCurve._();
1880
1881 /// An oscillating curve that first grows and then shrink in magnitude.
1882 ///
1883 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_bounce_in_out.mp4}
1884 static const Curve bounceInOut = _BounceInOutCurve._();
1885
1886 /// An oscillating curve that grows in magnitude while overshooting its bounds.
1887 ///
1888 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in.mp4}
1889 static const ElasticInCurve elasticIn = ElasticInCurve();
1890
1891 /// An oscillating curve that shrinks in magnitude while overshooting its bounds.
1892 ///
1893 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_out.mp4}
1894 static const ElasticOutCurve elasticOut = ElasticOutCurve();
1895
1896 /// An oscillating curve that grows and then shrinks in magnitude while overshooting its bounds.
1897 ///
1898 /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_elastic_in_out.mp4}
1899 static const ElasticInOutCurve elasticInOut = ElasticInOutCurve();
1900}
1901

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com