aboutsummaryrefslogtreecommitdiff
path: root/2025/2025-12-31-dropped-animation.html
diff options
context:
space:
mode:
authorhrbrmstr <bob@rud.is>2025-12-31 11:32:05 -0500
committerhrbrmstr <bob@rud.is>2025-12-31 11:32:05 -0500
commit7e628e7d5e1b7fb88b6e792d2052e55f259d5709 (patch)
tree45ce76fddb923adeeedae94401f94c33530115f4 /2025/2025-12-31-dropped-animation.html
parentb70adecfaab54d3ac747a7aa2f96b1169babcdd4 (diff)
add: dropped
Diffstat (limited to '2025/2025-12-31-dropped-animation.html')
-rw-r--r--2025/2025-12-31-dropped-animation.html190
1 files changed, 190 insertions, 0 deletions
diff --git a/2025/2025-12-31-dropped-animation.html b/2025/2025-12-31-dropped-animation.html
new file mode 100644
index 0000000..53a7f36
--- /dev/null
+++ b/2025/2025-12-31-dropped-animation.html
@@ -0,0 +1,190 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>2025 — Dropped</title>
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+ <link href="https://fonts.googleapis.com/css2?family=Londrina+Outline&display=swap" rel="stylesheet" />
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ body {
+ background: #0a0a0a;
+ min-height: 100vh;
+ overflow: hidden;
+ font-family: "Londrina Outline", system-ui, sans-serif;
+ }
+
+ .container {
+ position: relative;
+ width: 100%;
+ height: 100vh;
+ }
+
+ .year {
+ position: absolute;
+ top: 10%;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 0.5vw;
+ }
+
+ .digit {
+ font-size: 22vw;
+ font-weight: 400;
+ color: #fff;
+ line-height: 1;
+ position: relative;
+ }
+
+ .year.dropping {
+ animation: drop 1.4s cubic-bezier(0.55, 0, 1, 0.45) forwards;
+ }
+
+ @keyframes drop {
+ 0% {
+ top: 10%;
+ }
+ 100% {
+ top: calc(100vh - 24vw);
+ }
+ }
+
+ .shard {
+ position: absolute;
+ font-size: 22vw;
+ font-weight: 400;
+ color: #fff;
+ line-height: 1;
+ font-family: "Londrina Outline", system-ui, sans-serif;
+ clip-path: polygon(var(--clip));
+ animation: shard-fall var(--duration) cubic-bezier(0.25, 0, 0.5, 1) forwards;
+ animation-delay: var(--delay);
+ opacity: 1;
+ }
+
+ @keyframes shard-fall {
+ 0% {
+ transform: translate(0, 0) rotate(0deg);
+ opacity: 1;
+ }
+ 100% {
+ transform: translate(var(--tx), var(--ty)) rotate(var(--rot));
+ opacity: 0;
+ }
+ }
+
+ .restart {
+ position: fixed;
+ bottom: 30px;
+ left: 50%;
+ transform: translateX(-50%);
+ background: transparent;
+ border: 1px solid #333;
+ color: #666;
+ padding: 10px 24px;
+ font-size: 14px;
+ cursor: pointer;
+ transition: all 0.2s;
+ opacity: 0;
+ z-index: 50;
+ }
+
+ .restart.visible {
+ opacity: 1;
+ }
+
+ .restart:hover {
+ border-color: #fff;
+ color: #fff;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <div class="year" id="year">
+ <span class="digit" data-char="2">2</span>
+ <span class="digit" data-char="0">0</span>
+ <span class="digit" data-char="2">2</span>
+ <span class="digit" data-char="5">5</span>
+ </div>
+ </div>
+ <button class="restart" id="restart">drop again</button>
+
+ <script>
+ const year = document.getElementById("year");
+ const restartBtn = document.getElementById("restart");
+ const container = document.querySelector(".container");
+
+ const shardConfigs = [
+ { clip: "0% 0%, 50% 0%, 30% 50%, 0% 40%", tx: -120, ty: 180, rot: -25 },
+ { clip: "50% 0%, 100% 0%, 100% 35%, 60% 50%", tx: 80, ty: 200, rot: 35 },
+ { clip: "0% 40%, 30% 50%, 40% 100%, 0% 100%", tx: -90, ty: 250, rot: -40 },
+ { clip: "30% 50%, 60% 50%, 70% 100%, 40% 100%", tx: 20, ty: 300, rot: 10 },
+ { clip: "60% 50%, 100% 35%, 100% 100%, 70% 100%", tx: 100, ty: 220, rot: 45 },
+ ];
+
+ function createShards(digit, index) {
+ const rect = digit.getBoundingClientRect();
+ const char = digit.dataset.char;
+
+ shardConfigs.forEach((config, i) => {
+ const shard = document.createElement("span");
+ shard.className = "shard";
+ shard.textContent = char;
+ shard.style.left = rect.left + "px";
+ shard.style.top = rect.top + "px";
+ shard.style.setProperty("--clip", config.clip);
+ shard.style.setProperty("--tx", config.tx + (Math.random() - 0.5) * 60 + "px");
+ shard.style.setProperty("--ty", config.ty + Math.random() * 100 + "px");
+ shard.style.setProperty("--rot", config.rot + (Math.random() - 0.5) * 30 + "deg");
+ shard.style.setProperty("--duration", 1.2 + Math.random() * 0.6 + "s");
+ shard.style.setProperty("--delay", i * 0.02 + index * 0.05 + "s");
+ container.appendChild(shard);
+ });
+ }
+
+ function shatter() {
+ const digits = document.querySelectorAll(".digit");
+ digits.forEach((digit, index) => {
+ createShards(digit, index);
+ });
+
+ year.style.opacity = "0";
+
+ setTimeout(() => {
+ restartBtn.classList.add("visible");
+ }, 2000);
+ }
+
+ function startAnimation() {
+ document.querySelectorAll(".shard").forEach((s) => s.remove());
+ year.style.opacity = "1";
+ year.classList.remove("dropping", "shattered");
+ restartBtn.classList.remove("visible");
+
+ void year.offsetWidth;
+
+ year.classList.add("dropping");
+
+ year.addEventListener("animationend", function handler(e) {
+ if (e.animationName === "drop") {
+ year.removeEventListener("animationend", handler);
+ shatter();
+ }
+ });
+ }
+
+ restartBtn.addEventListener("click", startAnimation);
+
+ setTimeout(startAnimation, 500);
+ </script>
+ </body>
+</html>