Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Code-Nit-Whit/0f3effe6bc19eae611f43bfd7ee481c2 to your computer and use it in GitHub Desktop.
Save Code-Nit-Whit/0f3effe6bc19eae611f43bfd7ee481c2 to your computer and use it in GitHub Desktop.
Squares: Iterative Duplication & Recursive Agamogenesis

Squares: Iterative Duplication & Recursive Agamogenesis

A trippy CSS + JavaScript animation looping the duplication of rotating squares, then reducing the size of and nesting a child square element... and repeating. Threw a nice color changing gradient effect for good measure.

A Pen by Code_Nit_Whit on CodePen.

License.

<div id="container" class="container"></div>
//Had to write this one down for myself before I started, figured I'd leave it in for everyone else.
//Process:
// 1) Creates a square: reduces width relative the parent square so they barely touch when rotating, sets opacity to 1, applies square class with the css rotation animation, applies color gradient animaton, appends (nests) the new square to the last (now parent) square, and resets the variable for last square's width with the new one's for the next iteration.
// 2) Calles mutiplySquares, passing through the new square and a reference to the parent square. This function mutltiplies the nested nested square @ the same size as the original with a delay between each duplication that evens the rotational "spacing" of eah duplication. It also reverses the rotation direction of all these new squares and the original @ this level every other mutiplier function call. This effectively stops rotation of the reversed levels due to the compunding effect of the rotational duration being applied recursively doubling the rotational speed relative to the viewer. This also synchronizes the relative rotational velocity of the still spinning levels. Comment out line 100 where the boolean's value is toggled each iteration, and you will notice each level will double the previous level's speed of rotation.
// 3) At the same time this function starts, another square is being nested inside the one being worked on in multiplySquares... recursively nesting and multiplying squares at deeper and deeper levels.
// The entire process repeats a limited number of times due to obvious space limitations. May add scaling the topmost container which would be inherited to all nested elements allowing for further square creation and nesting and creating a 3D motion/zooming effect. This will require deleting the outtermost parent square each time a smaller set gets added to avoid permformance issues and a broken animation... But the animation is pretty fun to watch as it is. Please comment your link if you decide to do this youself! I'd love to see it.
const colors = [
"#ff0000ff",
"#ff2400ff",
"#ff4900ff",
"#ff6d00ff",
"#ff9100ff",
"#f1ad00ff",
"#e4c800ff",
"#d6e400ff",
"#c8ff00ff",
"#96ff16ff",
"#64ff2dff",
"#32ff43ff",
"#00ff59ff",
"#00d683ff",
"#00acacff",
"#0083d6ff",
"#0059ffff",
"#3743ffff",
"#6f2dffff",
"#a616ffff",
"#dd00ffff",
"#e600bfff",
"#ee0080ff",
"#f70040ff",
"#ff0000ff"
];
function startColorCycle(element) {
element.style.transition = "border-color 0.8s ease-in-out";
let currentColorIndex = 0;
function changeColor() {
element.style.borderColor = colors[currentColorIndex];
currentColorIndex = (currentColorIndex + 1) % colors.length;
}
setInterval(changeColor, 800);
}
function multiplySquares(childSquare, parentSquare, hammerTime) {
const maxMultIterations = 3;
let squareMultiplicationInteration = 0;
if (hammerTime) {
childSquare.style.animationDirection = "reverse";
console.log(childSquare.style.animationDirection);
}
const loopSquareMultiplication = setInterval(() => {
const squareDuplicate = document.createElement("div");
squareDuplicate.classList.add("square");
squareDuplicate.style.width = childSquare.style.width;
if (hammerTime) {
squareDuplicate.style.animationDirection = "reverse";
console.log(squareDuplicate.style.animationDirection);
}
startColorCycle(squareDuplicate);
squareDuplicate.style.opacity = 1;
parentSquare.append(squareDuplicate);
squareMultiplicationInteration++;
if (squareMultiplicationInteration > maxMultIterations) {
clearInterval(loopSquareMultiplication);
}
}, 795);
}
//going bigger newWidth = Math.sqrt((lastWidth * lastWidth) * 2);
//going smaller newWidth = Math.sqrt( (lastWidth*lastWidth) / 2 );
function creationStationPlay() {
let lastSquare;
const container = document.getElementById("container");
let lastWidth = 400;
let hammerTime = false;
function createNestedSquares() {
const newSquare = document.createElement("div");
const newWidth = Math.sqrt((lastWidth * lastWidth) / 2);
const newWidthString = `${newWidth}px`;
lastWidth = newWidth;
newSquare.classList.add("square");
newSquare.style.width = newWidthString;
newSquare.style.opacity = 1;
startColorCycle(newSquare);
if (!lastSquare) {
lastSquare = container;
container.append(newSquare);
} else {
lastSquare.append(newSquare);
}
multiplySquares(newSquare, lastSquare, hammerTime);
lastSquare = newSquare;
hammerTime = !hammerTime;
}
const iterationDuration = 500;
const maxIterations = 9;
let squareCreationInteration = 0;
const loopSquareCreation = setInterval(() => {
createNestedSquares();
squareCreationInteration++;
if (squareCreationInteration >= maxIterations) {
clearInterval(loopSquareCreation);
}
}, iterationDuration);
}
creationStationPlay();
#container {
width: 100vw;
height: 100vh;
overflow: scroll;
display: grid;
place-items: center;
background-color: black;
}
.square {
height: auto;
aspect-ratio: 1;
display: grid;
place-items: center;
position: absolute;
opacity: 0;
transition: opacity 2s ease-in-out;
border: 2px solid black;
animation: rotate 4s linear infinite;
animaton-play-state: running;
animation-direction: normal;
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment