Skip to content

Instantly share code, notes, and snippets.

@ja-k-e
Last active August 29, 2015 14:21
Show Gist options
  • Select an option

  • Save ja-k-e/d6e612b4be69b6f0e025 to your computer and use it in GitHub Desktop.

Select an option

Save ja-k-e/d6e612b4be69b6f0e025 to your computer and use it in GitHub Desktop.
Sass Golden Spiral Animation
- var _count = 8 * 4
section
div
figure.bg
- var i = 1
while i <= 12
span
- i++
figure
- var i = 1
while i <= _count
span
- i++

Sass Golden Spiral Animation

Exploring the limits of Sass and browser rendering.

Based on my static javascript/svg attempt Math isn't completely lined up yet. Relying on the wrong vars in some cases. You'll see if you try and scale to a non multiple of 4 iterations.

A Pen by Jake Albaugh on CodePen.

License.

// initial sequence
$sequence: 1,1;
// to be left values
$sequence_lefts: ();
// to be top values
$sequence_tops: ();
// how many iterations
// best when multiple of 4
// must match jade line 1
$iterations: 4;
// how many items per iteration
$per_iteration: 8;
// total item count
$count: $iterations * $per_iteration;
//
// creating the fibonacci sequence
//
// prepend function for big > small order
@function prepend($list, $value) {
@return join($value, $list);
}
// string replace method
// http://hugogiraudel.com/2014/01/13/sass-string-replacement-function/
// http://sassmeister.com/gist/1b4f2da5527830088e4d
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;
}
// reverse a list
@function reverse($list, $recursive: false) {
$result: ();
@for $i from length($list)*-1 through -1 {
@if type-of(nth($list, abs($i))) == list and $recursive {
$result: append($result, reverse(nth($list, abs($i)), $recursive));
}
@else {
$result: append($result, nth($list, abs($i)), comma);
}
}
@return $result;
}
// main commafy method
@function commafy($int) {
// get decimal
$decimal: $int - floor($int);
// round down
@if $decimal == 0 { $decimal: ""; }
@else { $decimal: str-replace(inspect($decimal),"0.", "."); }
$int: floor($int);
// empty array
$arr: ();
// empty comma string
$comma_int: "";
// integer to string
$int_str: inspect($int);
// if greater than 3
@if str-length($int_str) > 3 {
// pushing every three numbers to our array
$str_length: str-length($int_str);
// groups of three after first 1-2
@for $i from 1 through floor($str_length / 3) {
$slice_start: $str_length - ($i * 3) + 1;
$slice_end: $slice_start + 2;
$arr: append($arr, str-slice($int_str, $slice_start, $slice_end), comma);
}
// get first group of numbers if length not an even multiple of three
@if floor(str-length($int_str) / 3) != str-length($int_str) / 3 {
$diff: str-length($int_str) % 3;
$slice_start: 1;
$slice_end: $slice_start + $diff - 1;
$arr: append($arr, str-slice($int_str, $slice_start, $slice_end), comma);
}
// reverse the array
$arr: reverse($arr);
// removing spaces from array string
$comma_int: str-replace(inspect($arr), " ", "");
//$comma_int: inspect($arr);
} @else {
// three or less integers, just return the string
$comma_int: inspect($int_str);
}
// return commafied integer string plus potential decimal
@return $comma_int + $decimal;
}
// start at 3 (length of sequence plus 1)
// end at count (multiple of per_iteration)
@for $i from (length($sequence) + 1) through $count {
// get two previous values
$prev1: nth($sequence, (length($sequence) + 2 - $i));
$prev2: nth($sequence, (length($sequence) + 3 - $i));
// new value is sum of two previous values
$val: $prev1 + $prev2;
// add new value to sequence
$sequence: prepend($sequence, $val);
}
// origins
$origin_top: 0;
$origin_left: 0;
// getting left values
@for $i from 1 through length($sequence) {
// initial TR quarter
@if ($i == 1) {
$sequence_lefts: append($sequence_lefts, 0);
$sequence_tops: append($sequence_tops, 0);
// initial TL quarter
} @elseif ($i == 2) {
$sequence_lefts: append($sequence_lefts, nth($sequence, 1));
$sequence_tops: append($sequence_tops, nth($sequence, 3));
// initial BL quarter
} @elseif ($i == 3) {
$sequence_lefts: append($sequence_lefts, nth($sequence, 1) + nth($sequence, 4));
$sequence_tops: append($sequence_tops, 0);
// initial BR quarter
} @elseif ($i == 4) {
$sequence_lefts: append($sequence_lefts, nth($sequence, 1));
$sequence_tops: append($sequence_tops, 0);
// all other quarters
} @else {
// initial left, top
$left: 0; $top: 0;
// if TR
@if ($i % 4 == 1) {
$left: nth($sequence_lefts, $i - 4) + nth($sequence, $i - 4);
$top: nth($sequence_tops, $i - 1) + nth($sequence, $i - 1);
// if TL
} @elseif ($i % 4 == 2) {
$left: nth($sequence_lefts, $i - 1) + nth($sequence, $i - 1);
$top: nth($sequence_tops, $i - 4) - nth($sequence, $i);
// if BL
} @elseif ($i % 4 == 3) {
$left: nth($sequence_lefts, $i - 4) - nth($sequence, $i);
$top: nth($sequence_tops, $i - 3) + nth($sequence, $i - 3);
// if BR
} @elseif ($i % 4 == 0) {
$left: nth($sequence_lefts, $i - 3) + nth($sequence, $i - 3);
$top: nth($sequence_tops, $i - 4) + nth($sequence, $i - 4);
}
// if last first, set origins
@if ($i == length($sequence) - 8) {
$origin_top: $top;
$origin_left: $left;
}
// append left and top values
$sequence_lefts: append($sequence_lefts, $left);
$sequence_tops: append($sequence_tops, $top);
}
}
// power function
@function power($x, $n) {
$ret: 1;
@if $n >= 0 {
@for $i from 1 through $n {
$ret: $ret * $x;
}
} @else {
@for $i from $n to 0 {
$ret: $ret / $x;
}
}
@return $ret;
}
$golden_ratio: 1.6180339887498948482;
$golden_ratio_inverse: 1/$golden_ratio;
$container_w: 700px;
$fig_w: $container_w * power($golden_ratio,$iterations);
$fig_h: $fig_w * $golden_ratio_inverse;
$rel_origin_left: ($origin_left / (nth($sequence, 1) + nth($sequence, 2)) * 100%);
$rel_origin_top: ($origin_top / nth($sequence, 1) * 100%);
section {
width: $container_w;
height: $container_w * $golden_ratio_inverse;
position: absolute;
overflow: hidden;
left: calc(50% - #{$container_w / 2});
top: calc(50% - #{$container_w / 2 * $golden_ratio_inverse});
&::after {
border: 1px solid #444;
content: "";
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
> div {
position: absolute;
left: $rel_origin_left; top: $rel_origin_top;
}
}
figure {
background: #444;
width: $fig_w;
height: $fig_h;
position: absolute;
margin-left: $rel_origin_left / 100% * $fig_w * -1;
margin-top: $rel_origin_top / 100% * ($fig_w * $golden_ratio_inverse) * -1;
&:not(.bg) {
animation:
// slowing comes from relative scaling.
// linear slows towards the end. ease-in helps a little.
// crazy cubic bezier fix?
scale-fig 2s * $iterations ease-in infinite,
fade-fig 2s * $iterations ease-in infinite;
animation-delay: 2s;
}
transform: scale(power($golden_ratio_inverse, $iterations));
transform-origin: $rel_origin_left $rel_origin_top;
span {
display: block;
border-radius: 100% 0 0 0;
position: absolute;
&::after {
color: rgba(255,255,255,0.2);
position: absolute;
bottom: 2%; right: 2%;
}
}
}
@for $i from 1 through length($sequence) {
$ratio: nth($sequence, $i) / nth($sequence, 1);
$mod: $i % 4;
$mod_ratio: ($mod + 1) / 4;
span:nth-child(#{$i}) {
// square
width: $ratio * $fig_w * $golden_ratio_inverse;
padding-bottom: $ratio * $fig_w * $golden_ratio_inverse;
// positioning
left: nth($sequence_lefts, $i) / (nth($sequence, 1) + nth($sequence, 2)) * $fig_w;
top: nth($sequence_tops, $i) / nth($sequence, 1) * $fig_h;
// alternating grayscale
background-color: rgba(255,255,255,$mod_ratio);
// straight white
background-color: white;
// alternating color
background-color: hsl(360 - $mod_ratio * 360, 50, 50);
// rotations
@if ($i % 4 == 1) {
transform: rotate(-90deg);
} @elseif ($i % 4 == 2) {
transform: rotate(-180deg);
} @elseif ($i % 4 == 3) {
transform: rotate(-270deg);
} @elseif ($i % 4 == 0) {
transform: rotate(-360deg);
}
// after
&::after {
content: "#{commafy(nth($sequence, $i))}";
font-size: $iterations * 50px * $ratio;
}
}
}
// fading out at end, fading back in once zoomed out
@keyframes fade-fig {
0%, 80%, 95%, 100% { opacity: 1; }
90%, 91% { opacity: 0; }
}
// scaling in and out. timed with fade.
@keyframes scale-fig {
0%, 91%, 95%, 100% { transform: scale(power($golden_ratio_inverse, $iterations)); }
80%, 90% { transform: scale(power($golden_ratio, $iterations)); }
}
// the output
body::after {
content: "Generated Sequence: #{reverse($sequence)}";
position: absolute;
bottom: 24px;
left: calc(50% - #{$container_w/2});
width: $container_w;
color: #666;
text-align: center;
font-weight: light;
line-height: 1.8;
font-size: 12px;
}
// other shit
body { background: #222; }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment