//// /// Easing. /// /// A collection of easing functions which are commonly used for animations. /// This code is based on https://github.com/gre/bezier-easing. /// /// @group Easing /// /// @access public //// @use 'sass:math'; /// /// @access private /// $cubic-bezier-sample-pool: (); /// /// Sample pool size for cubic bezier calculations. /// $cubic-bezier-sample-pool-size: 10 !default; /// /// Minimum slope required to use the Newton-Raphson method for cubic bezier calculations. /// $cubic-bezier-newton-min-slope: .001 !default; /// /// Number of iterations of the Newton-Raphson method. /// $cubic-bezier-newton-iters: 4 !default; /// /// Precision of the subdivision method for cubic bezier calculations. /// $cubic-bezier-subdiv-precision: .0000001 !default; /// /// Maximum iterations of the subdivision method for cubic bezier calculations. /// $cubic-bezier-subdiv-max-iters: 10 !default; /// /// A cubic bezier function identical to the CSS cubic-bezier function. /// /// @param {number} $x1 - X of first point /// @param {number} $y1 - Y of first point /// @param {number} $x2 - X of second point /// @param {number} $y2 - Y of second point /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function cubic-bezier($x1, $y1, $x2, $y2, $x) { // // Cover simple cases // @if ($x1 == $y1) and ($x2 == $y2) { @return $x; } @if $x == 0 { @return 0; } @if $x == 1 { @return 1; } // // Generate samples // $sample-pool-key: $x1 + '_' + $x2; @if not map-has-key($cubic-bezier-sample-pool, $sample-pool-key) { $samples: (); @for $i from 0 through $cubic-bezier-sample-pool-size { $samples: append($samples, cubic-bezier-func($x1, $x2, math.div($i, $cubic-bezier-sample-pool-size))); } $cubic-bezier-sample-pool: map-merge($cubic-bezier-sample-pool, ($sample-pool-key: $samples)) !global; } // // Calculate cubic bezier // @return cubic-bezier-func($y1, $y2, cubic-bezier-t-for-x($x1, $x2, $x)); } /// /// @access private /// @function cubic-bezier-func-a($p1, $p2) { @return 1 - 3 * $p2 + 3 * $p1; } /// /// @access private /// @function cubic-bezier-func-b($p1, $p2) { @return 3 * $p2 - 6 * $p1; } /// /// @access private /// @function cubic-bezier-func-c($p1) { @return 3 * $p1; } /// /// One-dimensional cubic bezier function. /// /// @access private /// @function cubic-bezier-func($p1, $p2, $t) { @return ((cubic-bezier-func-a($p1, $p2) * $t + cubic-bezier-func-b($p1, $p2)) * $t + cubic-bezier-func-c($p1)) * $t; } /// /// Derivative of the one-dimensional cubic bezier function. /// /// @access private /// @function cubic-bezier-func-slope($p1, $p2, $t) { @return 3 * cubic-bezier-func-a($p1, $p2) * $t * $t + 2 * cubic-bezier-func-b($p1, $p2) * $t + cubic-bezier-func-c($p1); } /// /// Newton-Raphson method to calculate the t parameter for a given x parameter. /// /// @access private /// @function cubic-bezier-newton-raphson($x1, $x2, $x, $t) { @for $i from 1 through $cubic-bezier-newton-iters { $cur-slope: cubic-bezier-func-slope($x1, $x2, $t); @if $cur-slope == 0 { @return $t; } $cur-x: cubic-bezier-func($x1, $x2, $t) - $x; $t: $t - math.div($cur-x, $cur-slope); } @return $t; } /// /// Subdivision method to calculate the t parameter for a given x parameter. /// /// @access private /// @function cubic-bezier-binary-subdivide($x1, $x2, $x, $a, $b) { $cur-x: 0; $cur-t: 0; $i: 0; @while $i < $cubic-bezier-subdiv-max-iters { $cur-t: $a + ($b - $a) / 2; $cur-x: cubic-bezier-func($x1, $x2, $cur-t) - $x; @if $cur-x > 0 { $b: $cur-t; } @else { $a: $cur-t; } @if abs($cur-x) < $cubic-bezier-subdiv-precision { @return $cur-t; } } @return $cur-t; } /// /// Calculate the t parameter for a given x parameter. /// /// @access private /// @function cubic-bezier-t-for-x($x1, $x2, $x) { $sample-pool-key: $x1 + '_' + $x2; $samples: map-get($cubic-bezier-sample-pool, $sample-pool-key); $intv-start: 0; $cur-sample: 1; $last-sample: $cubic-bezier-sample-pool-size; @while ($cur-sample != $last-sample) and (nth($samples, $cur-sample) <= $x) { $intv-start: $intv-start + math.div(1, $cubic-bezier-sample-pool-size); $cur-sample: $cur-sample + 1; } $cur-sample: $cur-sample - 1; $dist: math.div($x - nth($samples, $cur-sample), nth($samples, $cur-sample + 1) - nth($samples, $cur-sample)); $guess-t: $intv-start + math.div($dist, $cubic-bezier-sample-pool-size); $init-slope: cubic-bezier-func-slope($x1, $x2, $guess-t); @if $init-slope >= $cubic-bezier-newton-min-slope { @return cubic-bezier-newton-raphson($x1, $x2, $x, $guess-t); } @else if $init-slope == 0 { @return $guess-t; } @else { @return cubic-bezier-binary-subdivide($x1, $x2, $x, $intv-start, $intv-start + 1 / $cubic-bezier-sample-pool-size); } } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease($x) { @return cubic-bezier(.25, .1, .25, 1, $x); } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in($x) { @return cubic-bezier(.42, 0, 1, 1, $x); } /// /// Sinusoidal easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out($x) { @return cubic-bezier(0, 0, .58, 1, $x); } /// /// Sinusoidal easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out($x) { @return cubic-bezier(.42, 0, .58, 1, $x); } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-sine($x) { @return cubic-bezier(.47, 0, .745, .715, $x); } /// /// Sinusoidal easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-sine($x) { @return cubic-bezier(.39, .575, .565, 1, $x); } /// /// Sinusoidal easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-sine($x) { @return cubic-bezier(.445, .05, .55, .95, $x); } /// /// Quadratic easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-quad($x) { @return cubic-bezier(.55, .085, .68, .53, $x); } /// /// Quadratic easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-quad($x) { @return cubic-bezier(.25, .46, .45, .94, $x); } /// /// Quadratic easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-quad($x) { @return cubic-bezier(.455, .03, .515, .955, $x); } /// /// Cubic easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-cubic($x) { @return cubic-bezier(.55, .055, .675, .19, $x); } /// /// Cubic easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-cubic($x) { @return cubic-bezier(.215, .61, .355, 1, $x); } /// /// Cubic easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-cubic($x) { @return cubic-bezier(.645, .045, .355, 1, $x); } /// /// Quart easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-quart($x) { @return cubic-bezier(.895, .03, .685, .22, $x); } /// /// Quart easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-quart($x) { @return cubic-bezier(.165, .84, .44, 1, $x); } /// /// Quart easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-quart($x) { @return cubic-bezier(.77, 0, .175, 1, $x); } /// /// Quint easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-quint($x) { @return cubic-bezier(.755, .05, .855, .06, $x); } /// /// Quint easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-quint($x) { @return cubic-bezier(.23, 1, .32, 1, $x); } /// /// Quint easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-quint($x) { @return cubic-bezier(.86, 0, .07, 1, $x); } /// /// Exponential easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-expo($x) { @return cubic-bezier(.95, .05, .795, .035, $x); } /// /// Exponential easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-expo($x) { @return cubic-bezier(.19, 1, .22, 1, $x); } /// /// Exponential easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-expo($x) { @return cubic-bezier(1, 0, 0, 1, $x); } /// /// Circular easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-circ($x) { @return cubic-bezier(.6, .04, .98, .335, $x); } /// /// Circular easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-out-circ($x) { @return cubic-bezier(.075, .82, .165, 1, $x); } /// /// Circular easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function ease-in-out-circ($x) { @return cubic-bezier(.785, .135, .15, .86, $x); }