@mixin interpolate($property, $min-screen, $min-value, $max-screen, $max-value, $easing: 'linear', $bending-points: 2) { // Default Easing 'Linear' $p0: 0; $p1: 0; $p2: 1; $p3: 1; // Parse Cubic Bezier string @if(str-slice($easing, 1, 12)=='cubic-bezier') { // Get the values between the brackets // TODO: Deal with different whitespace $i: str-index($easing, ')'); // Get index of closing bracket $values: str-slice($easing, 14, $i - 1); // Extract values between brackts $list: explode($values, ', '); // Split the values into a list @debug($list); // Cast values to numebrs $p0: number(nth($list, 1)); $p1: number(nth($list, 2)); $p2: number(nth($list, 3)); $p3: number(nth($list, 4)); } @if($easing=='ease') { $p0: 0.25; $p1: 1; $p2: 0.25; $p3: 1; } @if($easing=='ease-in-out') { $p0: 0.42; $p1: 0; $p2: 0.58; $p3: 1; } @if($easing=='ease-in') { $p0: 0.42; $p1: 0; $p2: 1; $p3: 1; } @if($easing=='ease-out') { $p0: 0; $p1: 0; $p2: 0.58; $p3: 1; } #{$property}: $min-value; @if($easing=='linear'or $bending-points < 1) { @media screen and (min-width: $min-screen) { #{$property}: calc-interpolation($min-screen, $min-value, $max-screen, $max-value); } } @else { // Loop through bending points $t: 1 / ($bending-points + 1); $i: 1; $prev-screen: $min-screen; $prev-value: $min-value; @while $t*$i <=1 { $bending-point: $t*$i; $value: cubic-bezier($p0, $p1, $p2, $p3, $bending-point); $screen-int: lerp($min-screen, $max-screen, $bending-point); $value-int: lerp($min-value, $max-value, $value); @media screen and (min-width: $prev-screen) { #{$property}: calc-interpolation($prev-screen, $prev-value, $screen-int, $value-int); } $prev-screen: $screen-int; $prev-value: $value-int; $i: $i+1; } } @media screen and (min-width:$max-screen) { #{$property}: $max-value; } } // Requires several helper functions including: pow, calc-interpolation, cubic-bezier, number and explode // Math functions: // Linear interpolations in CSS as a Sass function // Author: Mike Riethmuller | https://madebymike.com.au/writing/precise-control-responsive-typography/ I @function calc-interpolation($min-screen, $min-value, $max-screen, $max-value) { $a: ($max-value - $min-value) / ($max-screen - $min-screen); $b: $min-value - $a * $min-screen; $sign: "+"; @if ($b < 0) { $sign: "-"; $b: abs($b); } @return calc(#{$a*100}vw #{$sign} #{$b}); } // This is a crude Sass port webkits cubic-bezier function. Looking to simplify this if you can help. @function solve-bexier-x($p1x, $p1y, $p2x, $p2y, $x) { $cx: 3.0 * $p1x; $bx: 3.0 * ($p2x - $p1x) - $cx; $ax: 1.0 - $cx -$bx; $t0: 0.0; $t1: 1.0; $t2: $x; $x2: 0; $res: 1000; @while ($t0 < $t1 or $break) { $x2: (($ax * $t2 + $bx) * $t2 + $cx) * $t2; @if (abs($x2 - $x) < $res) { @return $t2; } @if ($x > $x2) { $t0: $t2; } @else { $t1: $t2; } $t2: ($t1 - $t0) * 0.5+$t0; } @return $t2; } @function cubic-bezier($p1x, $p1y, $p2x, $p2y, $x) { $cy: 3.0 * $p1y; $by: 3.0 * ($p2y - $p1y) - $cy; $ay: 1.0 - $cy - $by; $t: solve-bexier-x($p1x, $p1y, $p2x, $p2y, $x); @return (($ay * $t + $by) * $t + $cy) * $t; } // A stright up lerp // Credit: Ancient Greeks possibly Hipparchus of Rhodes @function lerp($a, $b, $t) { @return $a+($b - $a) * $t; } // String functions: // Cast string to number // Credit: Hugo Giraudel | https://www.sassmeister.com/gist/9fa19d254864f33d4a80 @function number($value) { @if type-of($value)=='number' { @return $value; } @else if type-of($value) !='string' { $_: log('Value for `to-number` should be a number or a string.'); } $result: 0; $digits: 0; $minus: str-slice($value, 1, 1)=='-'; $numbers: ('0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9); @for $i from if($minus, 2, 1) through str-length($value) { $character: str-slice($value, $i, $i); @if not (index(map-keys($numbers), $character) or $character=='.') { @return to-length(if($minus, -$result, $result), str-slice($value, $i)) } @if $character=='.' { $digits: 1; } @else if $digits==0 { $result: $result * 10 + map-get($numbers, $character); } @else { $digits: $digits * 10; $result: $result + map-get($numbers, $character) / $digits; } } @return if($minus, -$result, $result); ; } // Explode a string by a delimiter // Credit: https://gist.github.com/danielpchen/3677421ea15dcf2579ff @function explode($string, $delimiter) { $result: (); @if $delimiter=="" { @for $i from 1 through str-length($string) { $result: append($result, str-slice($string, $i, $i)); } @return $result; } $exploding: true; @while $exploding { $d-index: str-index($string, $delimiter); @if $d-index { @if $d-index>1 { $result: append($result, str-slice($string, 1, $d-index - 1)); $string: str-slice($string, $d-index + str-length($delimiter)); } @else if $d-index==1 { $string: str-slice($string, 1, $d-index + str-length($delimiter)); } @else { $result: append($result, $string); $exploding: false; } } @else { $result: append($result, $string); $exploding: false; } } @return $result; } @mixin respond($size) { $base: 16px; $newSize: ($size / $base) * 1em; @media screen and (max-width: $newSize) { @content; } }