Skip to content

Instantly share code, notes, and snippets.

@RobertAKARobin
Created July 6, 2021 08:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RobertAKARobin/2ac3807d7b96630dc5a040f2790d6cf5 to your computer and use it in GitHub Desktop.
Save RobertAKARobin/2ac3807d7b96630dc5a040f2790d6cf5 to your computer and use it in GitHub Desktop.
SCSS bounce mixin
@use 'sass:map';
@use 'sass:math';
@function roundTo($num, $places: 2) {
$factor: 100 * $places;
@return math.round($num * $factor) / $factor;
}
@mixin bounce(
$acceleration: null,
$bounciness: .5,
$direction: "bottom",
$distance: 100,
$name: bounce,
$timeFrom: 0,
$timeTotal: null,
) {
@if $bounciness >= 1 or $bounciness < 0 {
@error "$bounciness must be < 1 and > 0.";
}
@if ($acceleration == null) {
$acceleration: 28.9 - (4.8 * math.log($distance)); // Used trial and error to figure out what looks good at different distances
}
$acceleration: $distance * $acceleration;
$distanceMin: ($distance / 100);
$steps: (
(
"elapsed": 0,
"to": 0,
"direction": "down",
),
);
$step: 0;
$distanceThisBounce: $distance;
$timeThisBounce: 1;
@while $timeThisBounce >= .01 and $distanceThisBounce > 0 {
$timeThisBounce: math.sqrt((2 * $distanceThisBounce) / $acceleration);
$step: $step + $timeThisBounce;
$steps: append($steps, (
"elapsed": $step,
"to": $distanceThisBounce,
"direction": up,
));
$step: $step + $timeThisBounce;
$steps: append($steps, (
"elapsed": $step,
"to": 0,
"direction": down,
));
$distanceThisBounce: $distanceThisBounce * $bounciness;
$stepPrevious: $step;
}
$duration: $step;
@if $timeTotal == null {
$timeTotal: $duration;
}
@if ($duration + $timeFrom) > $timeTotal {
@error "Animation duration of #{$timeTotal} is less than required bounce duration of #{$duration + $timeFrom}.";
}
@keyframes #{$name} {
0% {
#{$direction}: 0;
}
@each $step in $steps {
$elapsed: map.get($step, "elapsed");
$elapsed: (($elapsed + $timeFrom) / $timeTotal);
$moveTo: map.get($step, "to") / 100;
#{roundTo(percentage($elapsed), 2)} {
#{$direction}: roundTo(percentage($moveTo), 2);
@if map.get($step, "direction") == up {
animation-timing-function: ease-in;
}
@else {
animation-timing-function: ease-out;
}
}
}
100% {
#{$direction}: 0;
}
}
.animation-#{$name} {
animation: $name #{$timeTotal}s linear infinite both;
}
}
@include bounce(
$direction: "left",
$distance: 100,
$timeFrom: 1,
$timeTotal: 5,
);
.box {
bottom: 0;
height: 500px;
outline: 1px solid black;
position: absolute;
width: 100px;
}
.ball {
background: #f00;
border-radius: 25px;
bottom: 0;
height: 50px;
left: 25px;
position: absolute;
width: 50px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment