codepens/generative-re-arranging-gri.../dist/script.js

195 lines
5.7 KiB
JavaScript

import {
randomBias,
randomSnap,
random,
createQtGrid,
map
} from "https://cdn.skypack.dev/@georgedoescode/generative-utils";
import debounce from "https://cdn.skypack.dev/debounce@1.2.1";
gsap.registerPlugin(Flip);
const gridElement = document.querySelector(".grid");
const width = 1920;
const height = 1080;
const mousePos = {
x: 0,
y: 0
};
let isAnimating = false;
// create an initial quadtree grid
let lastGrid = generateGrid({
x: random(0, width),
y: random(0, height)
});
// set the grid column/rows for each element in the grid and create the elements
lastGrid.areas.forEach((area, index) => {
const el = document.createElement("div");
el.style.gridColumn = `${area.col.start + 1} / ${area.col.end + 1}`;
el.style.gridRow = `${area.row.start + 1} / ${area.row.end + 1}`;
area.el = el;
gridElement.appendChild(el);
});
// drop some images onto the grid
const imageUrls = [
"https://images.unsplash.com/photo-1481253127861-534498168948?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NDc0OA&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1483959651481-dc75b89291f1?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NTI4OQ&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1525438160292-a4a860951216?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NDc0OA&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1563192504-36ac622196dd?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NDc5OA&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1509018877337-3af7dd307ea9?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NDgwOA&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1498322787346-775f77e49b2e?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NDgyNQ&ixlib=rb-1.2.1&q=85&w=1024",
"https://images.unsplash.com/photo-1532456745301-b2c645d8b80d?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYzOTU4NTE2NQ&ixlib=rb-1.2.1&q=85&w=1024"
];
for (let i = 0; i < imageUrls.length; i++) {
const area = random(lastGrid.areas.filter((a) => !a.taken));
area.el.style.backgroundImage = `url(${imageUrls[i]})`;
area.el.style.filter = "grayscale(1)";
// mark areas as "taken" so that they are not used twice
area.taken = true;
}
// add a little instruction to one of the cells
const instructionCell = random(lastGrid.areas.filter((a) => !a.taken));
instructionCell.taken = true;
instructionCell.el.innerHTML = "MOVE THE POINTER!";
instructionCell.el.classList.add("instruction");
// add a splash of color to one of the cells
const colorCell = random(lastGrid.areas.filter((a) => !a.taken));
colorCell.taken = true;
colorCell.el.style.background = "yellow";
// add some random words to the grid
["GSAP", "FLIP!", "GRIDS", "GENERATIVE"].map((word, idx) => {
const choice = random(lastGrid.areas.filter((a) => !a.taken));
choice.el.innerHTML = word;
choice.taken = true;
});
// add some shapes to all remaining areas
lastGrid.areas
.filter((a) => !a.taken)
.forEach((area) => {
if (random(0, 1) > 0.375) {
switch (random(["left", "right", "bottom", "top"])) {
case "top":
area.el.style.borderTop = "var(--stroke-width) solid #000";
break;
case "left":
area.el.style.borderLeft = "var(--stroke-width) solid #000";
break;
case "bottom":
area.el.style.borderBottom = "var(--stroke-width) solid #000";
break;
case "right":
area.el.style.borderRight = "var(--stroke-width) solid #000";
break;
}
} else {
if (random(0, 1) > 0.25) {
area.el.classList.add("circle");
if (random(0, 1) > 0.75) {
area.el.classList.add("circle--outline");
}
}
}
});
function updateItems() {
const focus = {
x: mousePos.x,
y: mousePos.y
};
// save the current state
const state = Flip.getState(".grid div", {
props: "backgroundImage"
});
// generate a new grid with the same amount of cells
let grid2;
while (grid2?.areas.length !== lastGrid.areas.length) {
grid2 = generateGrid(focus);
}
// update the grid cols/rows of each cell
gridElement.querySelectorAll("div").forEach((el, idx) => {
const area = grid2.areas[idx];
if (!area) return;
el.style.gridColumn = `${area.col.start + 1} / ${area.col.end + 1}`;
el.style.gridRow = `${area.row.start + 1} / ${area.row.end + 1}`;
});
// FLIP!
Flip.from(state, {
duration: 1.25,
ease: "power4.inOut",
absolute: true,
simple: false,
stagger: 0.01,
onComplete: (e) => {
isAnimating = false;
}
});
lastGrid = grid2;
}
// create a quadtree grid with random points/object centered around the mouse position
function generateGrid(focus) {
const points = [...Array(128)].map(() => {
return {
x: randomBias(0, width, focus.x, 1),
y: randomBias(0, height, focus.y, 1),
width: 1,
height: 1
};
});
return createQtGrid({ width, height, points, maxQtLevels: 4 });
}
// on mouse move, create a new quadtree grid centered around the pointer position and update
function onMouseMove(e) {
if (isAnimating) return;
const { clientX, clientY } = e;
mousePos.x = map(clientX, 0, window.innerWidth, 0, width);
mousePos.y = map(clientY, 0, window.innerHeight, 0, height);
updateItems();
isAnimating = true;
}
window.addEventListener("pointermove", debounce(onMouseMove, 250));
window.addEventListener("pointermove", (e) => {
gsap.to(".tracker", {
x: e.clientX,
y: e.clientY,
opacity: 1,
duration: 0.25,
ease: "sine.out",
transformOrigin: "50%"
});
});