| 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'; |
| 6 | library; |
| 7 | |
| 8 | import 'dart:math' as math; |
| 9 | import 'dart:ui'; |
| 10 | |
| 11 | import 'package:flutter/cupertino.dart'; |
| 12 | import 'package:flutter/foundation.dart'; |
| 13 | |
| 14 | export '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. |
| 26 | abstract 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 |
| 75 | abstract 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. |
| 117 | class _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} |
| 130 | class 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} |
| 158 | class 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} |
| 213 | class 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} |
| 270 | class 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. |
| 365 | class 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 | /// collinear 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. |
| 444 | class 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 | /// |
| 539 | abstract 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. |
| 667 | class 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. |
| 706 | class 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). |
| 928 | class 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. |
| 1235 | class 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. |
| 1258 | class _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 | |
| 1273 | double _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. |
| 1290 | class _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. |
| 1302 | class _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. |
| 1314 | class _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} |
| 1335 | class 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} |
| 1363 | class 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} |
| 1391 | class 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. |
| 1465 | abstract 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 | |