Pushing SCSS to its limits with math
I’ve always loved CSS and how well it performs with animations. I’ve had this one idea growing on me about creating visuals or landscapes created mathematically and be animated to some extent. Here is my first test on how well this works.
The Goal
There are several fancy JS libraries for creating stunning visuals, but I’ve never really been that hyped to do things via JS if you can do it with CSS. Thanks to having “long math” in high school, I remembered that you could create mathematical expressions and use that to generate visuals. For a first demo, I felt like a moving plane could be a good demo, so here are the specifications for the project:
- Moving plane (matrix of HTML items)
- Use mathematical expressions for some values
- Interactivity
The inspiration
There is a visual work of art in a shopping mall close to me where lightbulbs move up and down. A single lightbulb isn’t anything magnificent, but having them all work together to create objects is really awesome. This is what led me to think about how this could be done in code, and from there, how this could be done in CSS.
How we are going to achieve it
CSS is quite limited and since we need to do mathematical calculations, it would be better to have the calculations be done when the client is loading the page. This can be achieved if we do the logic in SCSS and generate the proper values. By creating a matrix of items, targetting them with CSS nth-selector and doing the expressional calculation depending on matrix dot index, we can achieve mathematically calculated visuals.
The code
Here is the part of the code that does the magic:
// Expression: x^2 + 4
@for $r from 0 through 9 {
$rowStart: ($r * 10 + 1);
@for $c from 0 through 9 {
$columnStart: $rowStart + $c;
// Work with row values
$valueX: pow($rowStart, 2) + 4;
@if $row == 0 {
$valueX: 0;
}
// Work with column values
$valueY: pow($columnStart, 2) + 4;
$valueY: 0;
@if $column == 0 {
$valueY: 0;
}
// Working on translate Z
div.dot:nth-child(#{$columnStart}) {
.dot__inner {
$hslColor: (($c + $r) * 15deg) % 360;
animation-delay: $c * 0.1s + $r * 0.2s;
animation-name: gradientWobble_ + $r + $c;
// Hue
background-color: hsl($hslColor, 50%, 50%);
@keyframes #{gradientWobble_ + $r + $c} {
0% {transform: translateZ(0px);}
50% {transform: translateZ(($valueX + $valueY) * 0.025px);}
100% {transform: translateZ(0px);}
}
}
}
}
}
We are using the simple mathematical expression (x^2 + 4) where x is the index of the elements. As we traverse the indexes, the result from the expression changes and therefore generates a different translateZ value.
The colors are not made in any fancy but they are also based on the index. We calculate the HUE with (index-X + index-Y) times a modifier. With this, we get a rising HUE-value that we can set to a value between 0 and 360 with the help of modulo (HSL-Value % 360).
These 40 lines of code generate approx 2000 lines of CSS with this 10x10 grid. As we made the grid bigger, CSS-size increases quickly.
Demo
You might notice that I’ve used some JS but that is only for generating necessary amount of HTML-elements as I’m too lazy to do copy-paste 100 times.
Future thoughts
One thing lacking from this demo is interactivity. I’ve been thinking about using SCSS maps for easier handling of variables and move from CSS Animations to CSS Transitions. With CSS Transitions you could have certain previous elements affect what values the matrix-items should have with eg. “:focus” state, this way we can stay away from JS 😃
See my other development projects
Bypassing shitnewspaper "Plus" subscription
I noticed one day that a certain shitnewspaper "Plus" service doesn't really need the paid subscription to read articles behind paywall.
Dipping my toes into jamstack
Testing the development experience of Hugo + Forestry CMS