219 lines
7.4 KiB
TypeScript
219 lines
7.4 KiB
TypeScript
import sunsetfourteenth from "/14thstsunrise.gif";
|
|
import backwalkgeorge from "/backwalkgeorge.jpg";
|
|
import ducklings from "/ducklings.gif";
|
|
import ermine from "/ermine.gif";
|
|
import fieldnotes from "/fieldnotes.gif";
|
|
import flowers from "/flowers.jpg";
|
|
import georgerasp from "/georgerasp.gif";
|
|
import georgetoilet from "/georgetoilet.jpg";
|
|
import heron from "/heron.jpg";
|
|
import klinefloor from "/klinefloor.jpg";
|
|
import klinemirror from "/klinemirror.jpg";
|
|
import klineoutside from "/klineoutside.jpg";
|
|
import klinetrain from "/klinetrain.jpg";
|
|
import lamp from "/lamp.gif";
|
|
import mirror from "/mirror.jpg";
|
|
import ohio from "/ohio.jpg";
|
|
import sashaklineburrito from "/sashaklineburrito.jpg";
|
|
import sashaklinecar from "/sashaklinecar.jpg";
|
|
import solderkitty from "/solderkitty.jpg";
|
|
import sunsetkline from "/sunsetkline.jpg";
|
|
|
|
const background = document.getElementById("background")!;
|
|
|
|
const prefersReducedMotion = window.matchMedia(
|
|
"(prefers-reduced-motion: reduce)",
|
|
).matches;
|
|
|
|
const setImages = () => {
|
|
let images: string[] = [];
|
|
let alts: string[] = [];
|
|
if (prefersReducedMotion) {
|
|
images = [
|
|
backwalkgeorge,
|
|
fieldnotes,
|
|
flowers,
|
|
georgetoilet,
|
|
heron,
|
|
klinefloor,
|
|
klinemirror,
|
|
klineoutside,
|
|
klinetrain,
|
|
mirror,
|
|
ohio,
|
|
sashaklineburrito,
|
|
sashaklinecar,
|
|
solderkitty,
|
|
sunsetkline,
|
|
];
|
|
alts = [
|
|
"A small child holding a stuffed monkey and a santa hat walking away from the camera",
|
|
"Animated gif of a notebook with a pretty landscape illustration cover",
|
|
"A field of violet flowers",
|
|
"A woman smiling next to a stuffed monkey",
|
|
"A heron standing in a pond",
|
|
"A woman in a green sweater and jeans sitting on the floor and smiling",
|
|
"A woman in bed taking a mirror selfie where her face is distorted",
|
|
"A selfie of a woman in a driveway next to a truck",
|
|
"A young girl smiling in front of a train",
|
|
"A shattered mirror",
|
|
"Two adjacent houses lit by an early morning sunrise",
|
|
"A selfie of two friends holding burritos, one of whom is wearing a cammo hat",
|
|
"A selfie of two friends in a car, one of whom is wearing a cammo hat",
|
|
"A pink porcelain kitty figurine supporting a soldering iron",
|
|
"A selfie of a woman indoors, behind her the sun is setting in the window",
|
|
];
|
|
} else {
|
|
images = [
|
|
sunsetfourteenth,
|
|
backwalkgeorge,
|
|
ducklings,
|
|
ermine,
|
|
fieldnotes,
|
|
flowers,
|
|
georgerasp,
|
|
georgetoilet,
|
|
heron,
|
|
klinefloor,
|
|
klinemirror,
|
|
klineoutside,
|
|
klinetrain,
|
|
lamp,
|
|
mirror,
|
|
ohio,
|
|
sashaklineburrito,
|
|
sashaklinecar,
|
|
solderkitty,
|
|
sunsetkline,
|
|
];
|
|
alts = [
|
|
"Animated gif of a sunrise on a city street",
|
|
"A small child holding a stuffed monkey and a santa hat walking away from the camera",
|
|
"Animated gif of a duck and ducklings swimming in a pond",
|
|
"Animated gif of an ermine peeking up out of a crack in concrete",
|
|
"Animated gif of a notebook with a pretty landscape illustration cover",
|
|
"A field of violet flowers",
|
|
"Animated gif of an amber CRT screen with flashing text and Raspberry Pi logo",
|
|
"A woman smiling next to a stuffed monkey",
|
|
"A heron standing in a pond",
|
|
"A woman in a green sweater and jeans sitting on the floor and smiling",
|
|
"A woman in bed taking a mirror selfie where her face is distorted",
|
|
"A selfie of a woman in a driveway next to a truck",
|
|
"A young girl smiling in front of a train",
|
|
"Animated gif of a lamp behind which cars drive past in the snow",
|
|
"A shattered mirror",
|
|
"Two adjacent houses lit by an early morning sunrise",
|
|
"A selfie of two friends holding burritos, one of whom is wearing a cammo hat",
|
|
"A selfie of two friends in a car, one of whom is wearing a cammo hat",
|
|
"A pink porcelain kitty figurine supporting a soldering iron",
|
|
"A selfie of a woman indoors, behind her the sun is setting in the window",
|
|
];
|
|
}
|
|
const randn_bm = (mean = 0, stdev = 0.4) => {
|
|
const u = 1 - Math.random();
|
|
const v = Math.random();
|
|
const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
|
|
return z * stdev + mean;
|
|
};
|
|
|
|
const randNums = (count: number, minDistance: number): number[] => {
|
|
const numbers: number[] = [];
|
|
|
|
function isFarEnough(num: number): boolean {
|
|
for (const n of numbers) {
|
|
if (Math.abs(num - n) < minDistance) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
while (numbers.length < count) {
|
|
const newNumber = randn_bm();
|
|
if (isFarEnough(newNumber)) {
|
|
numbers.push(newNumber);
|
|
}
|
|
}
|
|
|
|
return numbers;
|
|
};
|
|
|
|
const lefts = randNums(images.length, 0.05);
|
|
const tops = randNums(images.length, 0.05);
|
|
|
|
for (let index in images) {
|
|
const img = document.createElement("img");
|
|
img.setAttribute("src", images[index]);
|
|
img.setAttribute("alt", alts[index]);
|
|
let seedX = Math.random() * Math.random() * 100;
|
|
let seedY = Math.random() * Math.random() * 100;
|
|
img.setAttribute("seedX", seedX.toString());
|
|
img.setAttribute("seedY", seedY.toString());
|
|
const left = lefts[index];
|
|
const top = tops[index];
|
|
const z = (Math.abs(left) + Math.abs(top)) / 2;
|
|
const size = Math.random() * 5 + 9;
|
|
const range = { x: window.innerWidth * 0.7, y: window.innerHeight * 0.7 };
|
|
img.style.setProperty(
|
|
"left",
|
|
`${left * range.x + window.innerWidth / 2}px`,
|
|
);
|
|
img.style.setProperty("top", `${top * range.y + window.innerHeight / 2}px`);
|
|
img.style.setProperty("--translate-z", `${z * 3000}px`);
|
|
img.style.setProperty("inline-size", `${size}vmax`);
|
|
background.append(img);
|
|
}
|
|
};
|
|
|
|
const mobile = window.matchMedia("(hover: none) and (pointer: coarse)").matches;
|
|
|
|
if (!mobile) {
|
|
const root = document.documentElement;
|
|
const onMove = (ev: MouseEvent) => {
|
|
const rotateY = `${-(ev.clientX - window.innerWidth / 2) * 0.003}deg`;
|
|
const rotateX = `${(ev.clientY - window.innerHeight / 2) * 0.003}deg`;
|
|
root.style.setProperty("--rotate-x", rotateX);
|
|
root.style.setProperty("--rotate-y", rotateY);
|
|
};
|
|
document.addEventListener("mousemove", onMove);
|
|
}
|
|
|
|
window.addEventListener("load", () => {
|
|
background.setAttribute("loaded", "");
|
|
|
|
const noise = (t: number) => {
|
|
return Math.sin(2 * t) + Math.sin(Math.PI * t);
|
|
};
|
|
|
|
const toDomPrecision = (v: number) => {
|
|
return Math.round(v * 1e4) / 1e4;
|
|
};
|
|
|
|
const dpi = window.devicePixelRatio;
|
|
const moveImages = (t: number) => {
|
|
const background = document.getElementById("background")!;
|
|
const images = background.getElementsByTagName("img");
|
|
for (let i = 0; i < images.length; i++) {
|
|
const img = images[i];
|
|
let top = toDomPrecision(
|
|
Number(img.style.top.slice(0, img.style.top.length - 2)),
|
|
);
|
|
let left = toDomPrecision(
|
|
Number(img.style.left.slice(0, img.style.left.length - 2)),
|
|
);
|
|
top += (noise(Number(img.getAttribute("seedY")) + t) * 0.4) / dpi; // move faster on lower-res displays to improve jitter
|
|
left += (noise(Number(img.getAttribute("seedX")) + t) * 0.4) / dpi;
|
|
img.style.setProperty("top", `${top}px`);
|
|
img.style.setProperty("left", `${left}px`);
|
|
}
|
|
t += 0.0005;
|
|
window.requestAnimationFrame(moveImages.bind(this, t));
|
|
};
|
|
|
|
if (!prefersReducedMotion) {
|
|
moveImages(0);
|
|
}
|
|
});
|
|
|
|
setImages();
|