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

Provided by KDAB

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