//// /// 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 //// /// /// @access private /// $iro-cubic-bezier-sample-pool: (); /// /// Sample pool size for cubic bezier calculations. /// $iro-cubic-bezier-sample-pool-size: 10 !default; /// /// Minimum slope required to use the Newton-Raphson method for cubic bezier calculations. /// $iro-cubic-bezier-newton-min-slope: 0.001 !default; /// /// Number of iterations of the Newton-Raphson method. /// $iro-cubic-bezier-newton-iters: 4 !default; /// /// Precision of the subdivision method for cubic bezier calculations. /// $iro-cubic-bezier-subdiv-precision: 0.0000001 !default; /// /// Maximum iterations of the subdivision method for cubic bezier calculations. /// $iro-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 iro-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($iro-cubic-bezier-sample-pool, $sample-pool-key) { $samples: (); @for $i from 0 through $iro-cubic-bezier-sample-pool-size { $samples: append($samples, iro-cubic-bezier-func($x1, $x2, $i / $iro-cubic-bezier-sample-pool-size)); } $iro-cubic-bezier-sample-pool: map-merge($iro-cubic-bezier-sample-pool, ($sample-pool-key: $samples)) !global; } // // Calculate cubic bezier // @return iro-cubic-bezier-func($y1, $y2, iro-cubic-bezier-t-for-x($x1, $x2, $x)); } /// /// @access private /// @function iro-cubic-bezier-func-a($p1, $p2) { @return 1 - 3 * $p2 + 3 * $p1; } /// /// @access private /// @function iro-cubic-bezier-func-b($p1, $p2) { @return 3 * $p2 - 6 * $p1; } /// /// @access private /// @function iro-cubic-bezier-func-c($p1) { @return 3 * $p1; } /// /// One-dimensional cubic bezier function. /// /// @access private /// @function iro-cubic-bezier-func($p1, $p2, $t) { @return ((iro-cubic-bezier-func-a($p1, $p2) * $t + iro-cubic-bezier-func-b($p1, $p2)) * $t + iro-cubic-bezier-func-c($p1)) * $t; } /// /// Derivative of the one-dimensional cubic bezier function. /// /// @access private /// @function iro-cubic-bezier-func-slope($p1, $p2, $t) { @return 3 * iro-cubic-bezier-func-a($p1, $p2) * $t * $t + 2 * iro-cubic-bezier-func-b($p1, $p2) * $t + iro-cubic-bezier-func-c($p1); } /// /// Newton-Raphson method to calculate the t parameter for a given x parameter. /// /// @access private /// @function iro-cubic-bezier-newton-raphson($x1, $x2, $x, $t) { @for $i from 1 through $iro-cubic-bezier-newton-iters { $cur-slope: iro-cubic-bezier-func-slope($x1, $x2, $t); @if $cur-slope == 0 { @return $t; } $cur-x: iro-cubic-bezier-func($x1, $x2, $t) - $x; $t: $t - $cur-x / $cur-slope; } @return $t; } /// /// Subdivision method to calculate the t parameter for a given x parameter. /// /// @access private /// @function iro-cubic-bezier-binary-subdivide($x1, $x2, $x, $a, $b) { $cur-x: 0; $cur-t: 0; $i: 0; @while $i < $iro-cubic-bezier-subdiv-max-iters { $cur-t: $a + ($b - $a) / 2; $cur-x: iro-cubic-bezier-func($x1, $x2, $cur-t) - $x; @if $cur-x > 0 { $b: $cur-t; } @else { $a: $cur-t; } @if abs($cur-x) < $iro-cubic-bezier-subdiv-precision { @return $cur-t; } } @return $cur-t; } /// /// Calculate the t parameter for a given x parameter. /// /// @access private /// @function iro-cubic-bezier-t-for-x($x1, $x2, $x) { $sample-pool-key: $x1 + '_' + $x2; $samples: map-get($iro-cubic-bezier-sample-pool, $sample-pool-key); $intv-start: 0; $cur-sample: 1; $last-sample: $iro-cubic-bezier-sample-pool-size; @while ($cur-sample != $last-sample) and (nth($samples, $cur-sample) <= $x) { $intv-start: $intv-start + (1 / $iro-cubic-bezier-sample-pool-size); $cur-sample: $cur-sample + 1; } $cur-sample: $cur-sample - 1; $dist: ($x - nth($samples, $cur-sample)) / (nth($samples, $cur-sample + 1) - nth($samples, $cur-sample)); $guess-t: $intv-start + $dist / $iro-cubic-bezier-sample-pool-size; $init-slope: iro-cubic-bezier-func-slope($x1, $x2, $guess-t); @if $init-slope >= $iro-cubic-bezier-newton-min-slope { @return iro-cubic-bezier-newton-raphson($x1, $x2, $x, $guess-t); } @else if $init-slope == 0 { @return $guess-t; } @else { @return iro-cubic-bezier-binary-subdivide($x1, $x2, $x, $intv-start, $intv-start + 1 / $iro-cubic-bezier-sample-pool-size); } } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease($x) { @return iro-cubic-bezier(0.25, 0.1, 0.25, 1, $x); } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in($x) { @return iro-cubic-bezier(0.42, 0, 1, 1, $x); } /// /// Sinusoidal easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out($x) { @return iro-cubic-bezier(0, 0, 0.58, 1, $x); } /// /// Sinusoidal easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out($x) { @return iro-cubic-bezier(0.42, 0, 0.58, 1, $x); } /// /// Sinusoidal easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-sine($x) { @return iro-cubic-bezier(0.47, 0, 0.745, 0.715, $x); } /// /// Sinusoidal easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-sine($x) { @return iro-cubic-bezier(0.39, 0.575, 0.565, 1, $x); } /// /// Sinusoidal easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-sine($x) { @return iro-cubic-bezier(0.445, 0.05, 0.55, 0.95, $x); } /// /// Quadratic easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-quad($x) { @return iro-cubic-bezier(0.55, 0.085, 0.68, 0.53, $x); } /// /// Quadratic easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-quad($x) { @return iro-cubic-bezier(0.25, 0.46, 0.45, 0.94, $x); } /// /// Quadratic easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-quad($x) { @return iro-cubic-bezier(0.455, 0.03, 0.515, 0.955, $x); } /// /// Cubic easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-cubic($x) { @return iro-cubic-bezier(0.55, 0.055, 0.675, 0.19, $x); } /// /// Cubic easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-cubic($x) { @return iro-cubic-bezier(0.215, 0.61, 0.355, 1, $x); } /// /// Cubic easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-cubic($x) { @return iro-cubic-bezier(0.645, 0.045, 0.355, 1, $x); } /// /// Quart easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-quart($x) { @return iro-cubic-bezier(0.895, 0.03, 0.685, 0.22, $x); } /// /// Quart easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-quart($x) { @return iro-cubic-bezier(0.165, 0.84, 0.44, 1, $x); } /// /// Quart easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-quart($x) { @return iro-cubic-bezier(0.77, 0, 0.175, 1, $x); } /// /// Quint easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-quint($x) { @return iro-cubic-bezier(0.755, 0.05, 0.855, 0.06, $x); } /// /// Quint easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-quint($x) { @return iro-cubic-bezier(0.23, 1, 0.32, 1, $x); } /// /// Quint easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-quint($x) { @return iro-cubic-bezier(0.86, 0, 0.07, 1, $x); } /// /// Exponential easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-expo($x) { @return iro-cubic-bezier(0.95, 0.05, 0.795, 0.035, $x); } /// /// Exponential easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-expo($x) { @return iro-cubic-bezier(0.19, 1, 0.22, 1, $x); } /// /// Exponential easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-expo($x) { @return iro-cubic-bezier(1, 0, 0, 1, $x); } /// /// Circular easing function (in direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-circ($x) { @return iro-cubic-bezier(0.6, 0.04, 0.98, 0.335, $x); } /// /// Circular easing function (out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-out-circ($x) { @return iro-cubic-bezier(0.075, 0.82, 0.165, 1, $x); } /// /// Circular easing function (in-out direction). /// /// @param {number} $x - Progress between 0 and 1 inclusive /// /// @return {number} /// @function iro-ease-in-out-circ($x) { @return iro-cubic-bezier(0.785, 0.135, 0.15, 0.86, $x); }