Compare commits

...

8 Commits

38 changed files with 556 additions and 702 deletions

View File

@@ -21,15 +21,7 @@
color: #fff; color: #fff;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
text-shadow: 0 0 5px #000; text-shadow: 0 0 5px #000;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default;
-webkit-animation-name: leaf-fall, leaf-shake;
-webkit-animation-duration: 7s, 4s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
-webkit-user-select: none;
animation-name: leaf-fall, leaf-shake;
cursor: default; cursor: default;
animation-name: leaf-fall, leaf-shake; animation-name: leaf-fall, leaf-shake;
animation-duration: 7s, 4s; animation-duration: 7s, 4s;
@@ -41,15 +33,6 @@
.no-rotation { .no-rotation {
--rotate-start: 0deg !important; --rotate-start: 0deg !important;
--rotate-end: 0deg !important; --rotate-end: 0deg !important;
@-webkit-keyframes leaf-fall {
0% {
translate: 0 -10vh;
}
100% {
translate: 0 100vh;
}
}
} }
@@ -61,14 +44,6 @@
100% { 100% {
translate: 0 100vh; translate: 0 100vh;
} }
@-webkit-keyframes leaf-shake {
0%, 100% {
transform: translateX(0) rotate(var(--rotate-start, -20deg));
}
50% {
transform: translateX(80px) rotate(var(--rotate-end, 20deg));
}
}
} }

View File

@@ -5,12 +5,14 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 9999; z-index: 10;
overflow: hidden; overflow: hidden;
contain: strict; contain: strict;
contain: layout paint;
} }
.birthday-symbol { .birthday-symbol {
will-change: opacity;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@@ -29,7 +31,7 @@
} }
.birthday-inner { .birthday-inner {
pointer-events: auto; /* Allow hover over the actual item */ pointer-events: auto;
cursor: crosshair; cursor: crosshair;
display: inline-block; display: inline-block;
} }
@@ -138,7 +140,7 @@
opacity: 1; opacity: 1;
} }
100% { 100% {
transform: translateY(calc(var(--burst-y) + 150px)); /* Gravity pull downwards */ transform: translateY(calc(var(--burst-y) + 150px));
opacity: 0; opacity: 0;
} }
} }

View File

@@ -228,7 +228,19 @@ function createBirthday() {
// Ensure the burst container is appended to the main document body or the birthday container // Ensure the burst container is appended to the main document body or the birthday container
createBalloonPopConfetti(document.body, cx, cy, balloonColors[randomItem]); createBalloonPopConfetti(document.body, cx, cy, balloonColors[randomItem]);
} }
}, { once: true }); });
// Reset the balloon when it reappears at the bottom of the screen
symbol.addEventListener('animationiteration', function(e) {
// Ignore bubbling events from the inner sway animation
if (e.animationName === 'birthday-rise' || e.target === symbol) {
if (innerDiv.classList.contains('popped')) {
innerDiv.classList.remove('popped');
innerDiv.style.animation = '';
innerDiv.style.pointerEvents = 'auto';
}
}
});
} }
const startRot = (Math.random() * 20) - 10; // -10 to +10 spread const startRot = (Math.random() * 20) - 10; // -10 to +10 spread

View File

@@ -22,39 +22,13 @@
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
text-shadow: 0 0 5px #000; text-shadow: 0 0 5px #000;
user-select: none; user-select: none;
cursor: default; cursor: default;
-webkit-user-select: none;
-webkit-animation-name: christmas-fall, christmas-shake;
-webkit-animation-duration: 10s, 3s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
animation-name: christmas-fall, christmas-shake;
animation-name: christmas-fall, christmas-shake; animation-name: christmas-fall, christmas-shake;
animation-duration: 10s, 3s; animation-duration: 10s, 3s;
animation-timing-function: linear, ease-in-out; animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite; animation-iteration-count: infinite, infinite;
} }
@-webkit-keyframes christmas-fall {
0% {
translate: 0 -10vh;
}
100% {
translate: 0 110vh;
}
}
@-webkit-keyframes christmas-shake {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(80px);
}
}
@keyframes christmas-fall { @keyframes christmas-fall {

View File

@@ -7,9 +7,11 @@
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 1000;
overflow: hidden; overflow: hidden;
contain: layout paint;
} }
.earthday-meadow { .earthday-meadow {
will-change: transform;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -25,6 +27,7 @@
} }
.earthday-sway { .earthday-sway {
will-change: transform;
transform-origin: bottom center; transform-origin: bottom center;
animation: sway-grass 4s ease-in-out infinite alternate; animation: sway-grass 4s ease-in-out infinite alternate;
} }

View File

@@ -9,6 +9,7 @@
z-index: 10000; z-index: 10000;
contain: strict; contain: strict;
overflow: hidden; overflow: hidden;
contain: layout paint;
} }
.easter-grass-container { .easter-grass-container {
@@ -38,6 +39,7 @@
/* sway */ /* sway */
.easter-sway { .easter-sway {
will-change: transform;
transform-origin: bottom center; transform-origin: bottom center;
animation: easter-wind-sway 6s ease-in-out infinite alternate; animation: easter-wind-sway 6s ease-in-out infinite alternate;
} }

View File

@@ -197,6 +197,8 @@ function animateRabbit(rabbit) {
rabbit.style.transition = 'none'; rabbit.style.transition = 'none';
const transformScale = startFromLeft ? 'scaleX(-1)' : ''; const transformScale = startFromLeft ? 'scaleX(-1)' : '';
// Fix bounding box center-of-gravity shift when graphic is flipped
rabbit.style.transformOrigin = startFromLeft ? '59% 50%' : '50% 50%';
rabbit.style.transform = `translateX(${currentX}vw) ${transformScale}`; rabbit.style.transform = `translateX(${currentX}vw) ${transformScale}`;
const loopDurationMs = jumpDurationMs + pauseDurationMs; const loopDurationMs = jumpDurationMs + pauseDurationMs;
@@ -211,10 +213,10 @@ function animateRabbit(rabbit) {
if (!startTime) { if (!startTime) {
startTime = timestamp; startTime = timestamp;
// resetting gif, forces the browser to restart the GIF from the first frame (crucial for syncing hops with movement) // resetting gif, appending a timestamp cache-buster forces the browser
// to reload and start the GIF strictly from the first frame.
const currSrc = rabbit.src.split('?')[0]; const currSrc = rabbit.src.split('?')[0];
rabbit.src = ''; rabbit.src = currSrc + '?t=' + Date.now();
rabbit.src = currSrc;
} }
const elapsed = timestamp - startTime; const elapsed = timestamp - startTime;
@@ -243,6 +245,7 @@ function animateRabbit(rabbit) {
isAnimating = false; isAnimating = false;
rabbitTimeout = setTimeout(() => { rabbitTimeout = setTimeout(() => {
if (!document.body.contains(rabbit)) return;
animateRabbit(document.querySelector('#rabbit')); animateRabbit(document.querySelector('#rabbit'));
}, restTime); }, restTime);
return; return;

View File

@@ -9,6 +9,7 @@
z-index: 10; z-index: 10;
contain: strict; contain: strict;
overflow: hidden; overflow: hidden;
contain: layout paint;
} }
.eid-symbol { .eid-symbol {
@@ -18,12 +19,14 @@
} }
.eid-symbol.floating-star { .eid-symbol.floating-star {
will-change: opacity;
opacity: 0; opacity: 0;
animation: eid-twinkle 4s ease-in-out infinite; animation: eid-twinkle 4s ease-in-out infinite;
mix-blend-mode: screen; mix-blend-mode: screen;
} }
.lantern-rope { .lantern-rope {
will-change: transform;
position: absolute; position: absolute;
top: 0; top: 0;
width: 2px; width: 2px;

View File

@@ -22,8 +22,9 @@
/* Film grain */ /* Film grain */
.filmnoir-grain { .filmnoir-grain {
will-change: transform, opacity;
position: absolute; position: absolute;
top: -50%; top: 0;
left: -50%; left: -50%;
width: 200%; width: 200%;
height: 200%; height: 200%;
@@ -32,6 +33,7 @@
pointer-events: none; pointer-events: none;
mix-blend-mode: overlay; mix-blend-mode: overlay;
opacity: 0.3; opacity: 0.3;
translate: 0 -50vh;
} }
/* Vignette */ /* Vignette */
@@ -48,6 +50,7 @@
/* Occasional flicker and scratch */ /* Occasional flicker and scratch */
.filmnoir-scratches { .filmnoir-scratches {
will-change: opacity;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;

View File

@@ -11,9 +11,10 @@
} }
.rocket-trail { .rocket-trail {
will-change: transform;
position: absolute; position: absolute;
left: var(--trailX); left: var(--trailX);
top: var(--trailStartY); top: 0;
width: 4px; width: 4px;
/* activate the following for rocket trail */ /* activate the following for rocket trail */
@@ -28,6 +29,7 @@
box-shadow: 0 0 8px 2px white;*/ box-shadow: 0 0 8px 2px white;*/
animation: rocket-trail-animation 1s linear forwards; animation: rocket-trail-animation 1s linear forwards;
translate: 0 var(--trailStartY);
} }
@keyframes rocket-trail-animation { @keyframes rocket-trail-animation {
@@ -56,6 +58,7 @@
} }
.firework { .firework {
will-change: transform;
position: absolute; position: absolute;
width: 5px; width: 5px;
height: 5px; height: 5px;

View File

@@ -162,6 +162,7 @@ function startFireworks() {
} }
fireworksInterval = setInterval(() => { fireworksInterval = setInterval(() => {
if (!document.body.contains(fireworkContainer)) { clearInterval(fireworksInterval); return; }
const randomCount = Math.floor(Math.random() * maxFireworks) + minFireworks; const randomCount = Math.floor(Math.random() * maxFireworks) + minFireworks;
for (let i = 0; i < randomCount; i++) { for (let i = 0; i < randomCount; i++) {
setTimeout(() => { setTimeout(() => {

View File

@@ -61,12 +61,12 @@ function createFriday13(container) {
cat.parentNode.removeChild(cat); cat.parentNode.removeChild(cat);
} }
// Respawn with random delay between 5 to 25 seconds // Respawn with random delay between 5 to 25 seconds
setTimeout(spawnCat, Math.random() * 20000 + 5000); setTimeout(() => { if (document.body.contains(container)) spawnCat(); }, Math.random() * 20000 + 5000);
}, (catWalkDurationSeconds * 1000) + 500); // Wait for duration + 500ms safety margin }, (catWalkDurationSeconds * 1000) + 500); // Wait for duration + 500ms safety margin
} }
// Initial spawn with random delay // Initial spawn with random delay
setTimeout(spawnCat, Math.random() * 5000); setTimeout(() => { if (document.body.contains(container)) spawnCat(); }, Math.random() * 5000);
} }
function initializeFriday13() { function initializeFriday13() {

View File

@@ -9,16 +9,17 @@
z-index: 10; z-index: 10;
overflow: hidden; overflow: hidden;
contain: strict; contain: strict;
contain: layout paint;
} }
.frost-layer { .frost-layer {
will-change: transform;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
/* A glowing white-blue gradient from edges */
background: radial-gradient(ellipse at center, transparent 60%, rgba(180, 220, 255, 0.4) 100%); background: radial-gradient(ellipse at center, transparent 60%, rgba(180, 220, 255, 0.4) 100%);
box-shadow: inset 0 0 60px rgba(200, 230, 255, 0.5), inset 0 0 120px rgba(255, 255, 255, 0.3); box-shadow: inset 0 0 60px rgba(200, 230, 255, 0.5), inset 0 0 120px rgba(255, 255, 255, 0.3);
@@ -27,14 +28,13 @@
animation: frost-creep 4s ease-out forwards; animation: frost-creep 4s ease-out forwards;
} }
/* Subtle repeating star/crystal pattern */
.frost-crystals { .frost-crystals {
will-change: transform;
position: absolute; position: absolute;
top: -5%; top: 0;
left: -5%; left: -5%;
width: 110%; width: 110%;
height: 110%; height: 110%;
/* Use multi-layered star patterns for a random, crystalline spread */
background-image: background-image:
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"><circle cx="10" cy="10" r="1.5" fill="rgba(255,255,255,0.2)"/><circle cx="40" cy="30" r="1" fill="rgba(255,255,255,0.15)"/><circle cx="20" cy="50" r="2" fill="rgba(255,255,255,0.1)"/><path d="M50 10 L51 15 L56 16 L51 17 L50 22 L49 17 L44 16 L49 15 Z" fill="rgba(255,255,255,0.2)"/></svg>'), url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"><circle cx="10" cy="10" r="1.5" fill="rgba(255,255,255,0.2)"/><circle cx="40" cy="30" r="1" fill="rgba(255,255,255,0.15)"/><circle cx="20" cy="50" r="2" fill="rgba(255,255,255,0.1)"/><path d="M50 10 L51 15 L56 16 L51 17 L50 22 L49 17 L44 16 L49 15 Z" fill="rgba(255,255,255,0.2)"/></svg>'),
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40"><circle cx="5" cy="20" r="1" fill="rgba(255,255,255,0.15)"/><circle cx="25" cy="5" r="1.5" fill="rgba(255,255,255,0.1)"/><path d="M20 20 L21 23 L24 24 L21 25 L20 28 L19 25 L16 24 L19 23 Z" fill="rgba(255,255,255,0.15)"/></svg>'), url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40"><circle cx="5" cy="20" r="1" fill="rgba(255,255,255,0.15)"/><circle cx="25" cy="5" r="1.5" fill="rgba(255,255,255,0.1)"/><path d="M20 20 L21 23 L24 24 L21 25 L20 28 L19 25 L16 24 L19 23 Z" fill="rgba(255,255,255,0.15)"/></svg>'),
@@ -43,12 +43,9 @@
background-size: 110px 110px, 60px 60px, 30px 30px; background-size: 110px 110px, 60px 60px, 30px 30px;
background-position: 0 0, 15px 15px, 5px 10px; background-position: 0 0, 15px 15px, 5px 10px;
mix-blend-mode: overlay; mix-blend-mode: overlay;
/* Mask out the center so crystals only appear strongly on the edges */
-webkit-mask-image: radial-gradient(ellipse at center, transparent 50%, black 100%);
mask-image: radial-gradient(ellipse at center, transparent 50%, black 100%); mask-image: radial-gradient(ellipse at center, transparent 50%, black 100%);
animation: frost-shimmer 6s infinite alternate ease-in-out; animation: frost-shimmer 6s infinite alternate ease-in-out;
translate: 0 -5vh;
} }
@keyframes frost-creep { @keyframes frost-creep {

View File

@@ -7,25 +7,17 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
z-index: 10000; z-index: 10;
contain: layout paint; contain: layout paint;
} }
.halloween { .halloween {
will-change: transform;
position: fixed; position: fixed;
bottom: -10%; bottom: -10%;
z-index: 15; z-index: 15;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default; cursor: default;
-webkit-animation-name: halloween-fall, halloween-shake;
-webkit-animation-duration: 10s, 3s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
-webkit-animation-play-state: running, running;
animation-name: halloween-fall, halloween-shake; animation-name: halloween-fall, halloween-shake;
animation-duration: 10s, 3s; animation-duration: 10s, 3s;
animation-timing-function: linear, ease-in-out; animation-timing-function: linear, ease-in-out;
@@ -33,29 +25,7 @@
animation-play-state: running, running animation-play-state: running, running
} }
@-webkit-keyframes halloween-fall {
0% {
bottom: -10%;
}
100% {
bottom: 110%;
}
}
@-webkit-keyframes halloween-shake {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0)
}
50% {
-webkit-transform: translateX(80px);
transform: translateX(80px)
}
}
@keyframes halloween-fall { @keyframes halloween-fall {
0% { 0% {
@@ -81,73 +51,61 @@
.halloween:nth-of-type(0) { .halloween:nth-of-type(0) {
left: 1%; left: 1%;
-webkit-animation-delay: 0s, 0s;
animation-delay: 0s, 0s; animation-delay: 0s, 0s;
} }
.halloween:nth-of-type(1) { .halloween:nth-of-type(1) {
left: 10%; left: 10%;
-webkit-animation-delay: -1s, -1s;
animation-delay: -1s, -1s; animation-delay: -1s, -1s;
} }
.halloween:nth-of-type(2) { .halloween:nth-of-type(2) {
left: 20%; left: 20%;
-webkit-animation-delay: -2s, -2s;
animation-delay: -2s, -2s; animation-delay: -2s, -2s;
} }
.halloween:nth-of-type(3) { .halloween:nth-of-type(3) {
left: 30%; left: 30%;
-webkit-animation-delay: -3s, -3s;
animation-delay: -3s, -3s; animation-delay: -3s, -3s;
} }
.halloween:nth-of-type(4) { .halloween:nth-of-type(4) {
left: 40%; left: 40%;
-webkit-animation-delay: -4s, -4s;
animation-delay: -4s, -4s; animation-delay: -4s, -4s;
} }
.halloween:nth-of-type(5) { .halloween:nth-of-type(5) {
left: 50%; left: 50%;
-webkit-animation-delay: -5s, -5s;
animation-delay: -5s, -5s; animation-delay: -5s, -5s;
} }
.halloween:nth-of-type(6) { .halloween:nth-of-type(6) {
left: 60%; left: 60%;
-webkit-animation-delay: -6s, -6s;
animation-delay: -6s, -6s; animation-delay: -6s, -6s;
} }
.halloween:nth-of-type(7) { .halloween:nth-of-type(7) {
left: 70%; left: 70%;
-webkit-animation-delay: -7s, -7s;
animation-delay: -7s, -7s; animation-delay: -7s, -7s;
} }
.halloween:nth-of-type(8) { .halloween:nth-of-type(8) {
left: 80%; left: 80%;
-webkit-animation-delay: -8s, -8s;
animation-delay: -8s, -8s; animation-delay: -8s, -8s;
} }
.halloween:nth-of-type(9) { .halloween:nth-of-type(9) {
left: 90%; left: 90%;
-webkit-animation-delay: -9s, -9s;
animation-delay: -9s, -9s; animation-delay: -9s, -9s;
} }
.halloween:nth-of-type(10) { .halloween:nth-of-type(10) {
left: 25%; left: 25%;
-webkit-animation-delay: -10s, -10s;
animation-delay: -10s, -10s; animation-delay: -10s, -10s;
} }
.halloween:nth-of-type(11) { .halloween:nth-of-type(11) {
left: 65%; left: 65%;
-webkit-animation-delay: -11s, -11s;
animation-delay: -11s, -11s; animation-delay: -11s, -11s;
} }
@@ -162,7 +120,6 @@
z-index: 1000; z-index: 1000;
overflow: hidden; overflow: hidden;
mask-image: linear-gradient(to top, black, transparent); mask-image: linear-gradient(to top, black, transparent);
-webkit-mask-image: linear-gradient(to top, black, transparent);
} }
.halloween-fog-blob { .halloween-fog-blob {
position: absolute; position: absolute;
@@ -174,10 +131,12 @@
filter: blur(15px); filter: blur(15px);
} }
.halloween-fog-blob:nth-child(1) { .halloween-fog-blob:nth-child(1) {
will-change: transform;
left: -20vw; left: -20vw;
animation: fog-float1 25s ease-in-out infinite alternate; animation: fog-float1 25s ease-in-out infinite alternate;
} }
.halloween-fog-blob:nth-child(2) { .halloween-fog-blob:nth-child(2) {
will-change: transform;
left: -50vw; left: -50vw;
background: radial-gradient(ellipse at center, rgba(100, 110, 120, 0.3) 0%, transparent 65%); background: radial-gradient(ellipse at center, rgba(100, 110, 120, 0.3) 0%, transparent 65%);
animation: fog-float2 35s ease-in-out infinite alternate; animation: fog-float2 35s ease-in-out infinite alternate;
@@ -196,7 +155,7 @@
/* --- Spiders --- */ /* --- Spiders --- */
.halloween-spider-wrapper { .halloween-spider-wrapper {
position: absolute; position: absolute;
top: -50px; top: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@@ -204,8 +163,10 @@
transform-origin: top; transform-origin: top;
will-change: transform; will-change: transform;
pointer-events: auto; pointer-events: auto;
padding: 20px; /* Increase hit area safely */ padding: 20px; /* Increase hit area */
translate: 0 -50px;
} }
.halloween-thread { .halloween-thread {
width: 30px; /* Wider hit area for mouse interaction */ width: 30px; /* Wider hit area for mouse interaction */
height: 100vh; height: 100vh;
@@ -223,12 +184,12 @@
background: linear-gradient(to bottom, rgba(200, 200, 200, 0.1), rgba(200, 200, 200, 0.6)); background: linear-gradient(to bottom, rgba(200, 200, 200, 0.1), rgba(200, 200, 200, 0.6));
} }
.halloween-spider { .halloween-spider {
will-change: transform;
animation: spider-swing 3s ease-in-out infinite alternate; animation: spider-swing 3s ease-in-out infinite alternate;
transform-origin: top center; transform-origin: top center;
} }
/* MARK: SPIDER SWAY CONFIGURATION */ /* MARK: SPIDER SWAY CONFIGURATION */
/* Adjust degrees in 'rotate(...)' to change how far spider and thread swing in wind. */
@keyframes wind-sway { @keyframes wind-sway {
0% { transform: rotate(0deg); } 0% { transform: rotate(0deg); }
25% { transform: rotate(2deg); } 25% { transform: rotate(2deg); }

View File

@@ -182,14 +182,18 @@ function createSpider(container) {
setTimeout(() => { setTimeout(() => {
wrapper.remove(); wrapper.remove();
setTimeout(() => createSpider(container), Math.random() * 5000 + 1000); if (document.body.contains(container)) {
setTimeout(() => createSpider(container), Math.random() * 5000 + 1000);
}
}, 500); }, 500);
}); });
wrapper.addEventListener('animationend', () => { wrapper.addEventListener('animationend', () => {
if (isRetreating) return; if (isRetreating) return;
wrapper.remove(); wrapper.remove();
setTimeout(() => createSpider(container), Math.random() * 5000 + 1000); if (document.body.contains(container)) {
setTimeout(() => createSpider(container), Math.random() * 5000 + 1000);
}
}); });
container.appendChild(wrapper); container.appendChild(wrapper);
@@ -223,7 +227,9 @@ function createMouse(container) {
mouse.addEventListener('animationend', () => { mouse.addEventListener('animationend', () => {
mouse.remove(); mouse.remove();
setTimeout(() => createMouse(container), Math.random() * 4000 + 2000); if (document.body.contains(container)) {
setTimeout(() => createMouse(container), Math.random() * 4000 + 2000);
}
}); });
container.appendChild(mouse); container.appendChild(mouse);

View File

@@ -12,48 +12,21 @@
} }
.heart { .heart {
will-change: transform;
position: fixed; position: fixed;
bottom: -10%; bottom: -10%;
z-index: 15; z-index: 15;
-webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default; cursor: default;
-webkit-animation-name: heart-fall, heart-shake;
-webkit-animation-duration: 14s, 5s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
animation-name: heart-fall, heart-shake; animation-name: heart-fall, heart-shake;
animation-duration: 14s, 5s; animation-duration: 14s, 5s;
animation-timing-function: linear, ease-in-out; animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite; animation-iteration-count: infinite, infinite;
} }
@-webkit-keyframes heart-fall {
0% {
bottom: -10%;
}
100% {
bottom: 110%;
}
}
@-webkit-keyframes heart-shake {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0)
}
50% {
-webkit-transform: translateX(80px);
transform: translateX(80px)
}
}
@keyframes heart-fall { @keyframes heart-fall {
0% { 0% {
@@ -79,72 +52,60 @@
.heart:nth-of-type(0) { .heart:nth-of-type(0) {
left: 1%; left: 1%;
-webkit-animation-delay: 0s, 0s;
animation-delay: 0s, 0s animation-delay: 0s, 0s
} }
.heart:nth-of-type(1) { .heart:nth-of-type(1) {
left: 10%; left: 10%;
-webkit-animation-delay: 1s, 1s;
animation-delay: 1s, 1s animation-delay: 1s, 1s
} }
.heart:nth-of-type(2) { .heart:nth-of-type(2) {
left: 20%; left: 20%;
-webkit-animation-delay: 6s, .5s;
animation-delay: 6s, .5s animation-delay: 6s, .5s
} }
.heart:nth-of-type(3) { .heart:nth-of-type(3) {
left: 30%; left: 30%;
-webkit-animation-delay: 4s, 2s;
animation-delay: 4s, 2s animation-delay: 4s, 2s
} }
.heart:nth-of-type(4) { .heart:nth-of-type(4) {
left: 40%; left: 40%;
-webkit-animation-delay: 2s, 2s;
animation-delay: 2s, 2s animation-delay: 2s, 2s
} }
.heart:nth-of-type(5) { .heart:nth-of-type(5) {
left: 50%; left: 50%;
-webkit-animation-delay: 8s, 3s;
animation-delay: 8s, 3s animation-delay: 8s, 3s
} }
.heart:nth-of-type(6) { .heart:nth-of-type(6) {
left: 60%; left: 60%;
-webkit-animation-delay: 6s, 2s;
animation-delay: 6s, 2s animation-delay: 6s, 2s
} }
.heart:nth-of-type(7) { .heart:nth-of-type(7) {
left: 70%; left: 70%;
-webkit-animation-delay: 2.5s, 1s;
animation-delay: 2.5s, 1s animation-delay: 2.5s, 1s
} }
.heart:nth-of-type(8) { .heart:nth-of-type(8) {
left: 80%; left: 80%;
-webkit-animation-delay: 1s, 0s;
animation-delay: 1s, 0s animation-delay: 1s, 0s
} }
.heart:nth-of-type(9) { .heart:nth-of-type(9) {
left: 90%; left: 90%;
-webkit-animation-delay: 3s, 1.5s;
animation-delay: 3s, 1.5s animation-delay: 3s, 1.5s
} }
.heart:nth-of-type(10) { .heart:nth-of-type(10) {
left: 25%; left: 25%;
-webkit-animation-delay: 2s, 0s;
animation-delay: 2s, 0s animation-delay: 2s, 0s
} }
.heart:nth-of-type(11) { .heart:nth-of-type(11) {
left: 65%; left: 65%;
-webkit-animation-delay: 4s, 2.5s;
animation-delay: 4s, 2.5s animation-delay: 4s, 2.5s
} }

View File

@@ -29,11 +29,13 @@
} }
.mario-jump { .mario-jump {
will-change: transform;
animation: jump-arc 0.8s ease-in-out; animation: jump-arc 0.8s ease-in-out;
} }
/* 8-bit coin styling */ /* 8-bit coin styling */
.mario-coin { .mario-coin {
will-change: transform;
position: absolute; position: absolute;
width: 32px; width: 32px;
height: 32px; height: 32px;
@@ -47,11 +49,12 @@
.mario-coin::after { .mario-coin::after {
content: ''; content: '';
position: absolute; position: absolute;
top: 6px; top: 0;
left: 10px; left: 10px;
width: 4px; width: 4px;
height: 12px; height: 12px;
background: #daa520; background: #daa520;
translate: 0 6px;
} }
@keyframes mario-run { @keyframes mario-run {

View File

@@ -51,8 +51,9 @@ function createMarioDay(container) {
container.appendChild(wrapper); container.appendChild(wrapper);
// Periodically throw out an 8-bit coin // Periodically throw out an 8-bit coin
setInterval(() => { const intervalId = setInterval(() => {
if (!document.querySelector('.marioday-container')) return; if (!document.body.contains(container)) { clearInterval(intervalId); return; }
if (container.style.display === 'none') return;
const coin = document.createElement('div'); const coin = document.createElement('div');
coin.className = 'mario-coin'; coin.className = 'mario-coin';

View File

@@ -5,7 +5,7 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 10;
overflow: hidden; overflow: hidden;
contain: layout paint; contain: layout paint;
} }

View File

@@ -136,6 +136,7 @@ function createElements() {
for(let i=0; i<maxTrails; i++) trails.push(new Trail()); for(let i=0; i<maxTrails; i++) trails.push(new Trail());
function loop() { function loop() {
if (!document.body.contains(container)) { clearInterval(window.matrixInterval); return; }
if (isHidden) return; // Pause drawing when hidden if (isHidden) return; // Pause drawing when hidden
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = 'bold ' + fontSize + 'px monospace'; ctx.font = 'bold ' + fontSize + 'px monospace';

View File

@@ -12,13 +12,15 @@
} }
.oktoberfest-symbol { .oktoberfest-symbol {
will-change: transform;
position: absolute; position: absolute;
top: -10%; top: 0;
font-size: 2.2em; font-size: 2.2em;
user-select: none; user-select: none;
animation-name: oktoberfest-fall, oktoberfest-sway; animation-name: oktoberfest-fall, oktoberfest-sway;
animation-timing-function: linear, ease-in-out; animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite; animation-iteration-count: infinite, infinite;
translate: 0 -10vh;
} }
@keyframes oktoberfest-fall { @keyframes oktoberfest-fall {

View File

@@ -12,10 +12,11 @@
.olympia-symbol { .olympia-symbol {
position: absolute; position: absolute;
top: -10vh; top: 0;
opacity: 0.95; opacity: 0.95;
text-shadow: 0 0 10px rgba(255,255,255,0.2); text-shadow: 0 0 10px rgba(255,255,255,0.2);
z-index: 40; z-index: 40;
translate: 0 -10vh;
} }
.olympia-flame { .olympia-flame {
@@ -34,8 +35,8 @@
.olympia-ring-css::before { .olympia-ring-css::before {
content: ''; content: '';
position: absolute; position: absolute;
top: 50%; left: 50%; top: 0;
transform: translate(-50%, -50%); translate: -50% -50%;
width: 30px; width: 30px;
height: 30px; height: 30px;
border: 5px solid #0081C8; /* Default blue ring */ border: 5px solid #0081C8; /* Default blue ring */
@@ -46,13 +47,15 @@
} }
.olympia-symbol { .olympia-symbol {
position: absolute; position: absolute;
top: -10vh; top: 0;
opacity: 0.95; opacity: 0.95;
text-shadow: 0 0 10px rgba(255,255,255,0.2); text-shadow: 0 0 10px rgba(255,255,255,0.2);
z-index: 40; z-index: 40;
translate: 0 -10vh;
} }
.olympia-inner { .olympia-inner {
will-change: transform;
display: inline-block; display: inline-block;
animation: olympia-sway linear infinite alternate; animation: olympia-sway linear infinite alternate;
} }

View File

@@ -6,7 +6,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
z-index: 10; /* Behind popups but over background */ z-index: 10;
contain: strict; contain: strict;
overflow: hidden; overflow: hidden;
} }
@@ -30,8 +30,9 @@
} }
.oscar-spotlight { .oscar-spotlight {
will-change: transform;
position: absolute; position: absolute;
top: -10vh; top: 0;
/* MARK: SPOTLIGHT WIDTH CONFIGURATION */ /* MARK: SPOTLIGHT WIDTH CONFIGURATION */
/* To adjust bottom width (spread), change 'width' property (e.g., 20vw for narrow, 40vw for wide). */ /* To adjust bottom width (spread), change 'width' property (e.g., 20vw for narrow, 40vw for wide). */
/* To adjust top width (origin), modify first two percentages in 'clip-path' (e.g., 48% 0, 52% 0 for a very thin start). */ /* To adjust top width (origin), modify first two percentages in 'clip-path' (e.g., 48% 0, 52% 0 for a very thin start). */
@@ -42,9 +43,11 @@
transform-origin: top center; transform-origin: top center;
animation: spotlight-sweep 12s infinite alternate ease-in-out; animation: spotlight-sweep 12s infinite alternate ease-in-out;
mix-blend-mode: screen; mix-blend-mode: screen;
translate: 0 -10vh;
} }
.oscar-flash { .oscar-flash {
will-change: transform;
position: absolute; position: absolute;
width: 10px; width: 10px;
height: 10px; height: 10px;

View File

@@ -56,9 +56,9 @@ function createOscar(container) {
container.appendChild(carpet); container.appendChild(carpet);
container.appendChild(spotlights); container.appendChild(spotlights);
// Paparazzi flashes with randomized intervals
function flashLoop() { function flashLoop() {
if (!document.querySelector('.oscar-container')) { if (!document.body.contains(container)) return; // Kill the loop if container is removed
if (container.style.display === 'none') {
setTimeout(flashLoop, 1000); // Check again later if hidden setTimeout(flashLoop, 1000); // Check again later if hidden
return; return;
} }

View File

@@ -5,7 +5,7 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 9999; z-index: 10;
overflow: hidden; overflow: hidden;
contain: layout paint; contain: layout paint;
} }

View File

@@ -1,4 +1,3 @@
// 1. Read Configuration
const config = window.SeasonalsPluginConfig?.Pride || {}; const config = window.SeasonalsPluginConfig?.Pride || {};
const enabled = config.EnablePride !== undefined ? config.EnablePride : true; const enabled = config.EnablePride !== undefined ? config.EnablePride : true;
@@ -8,8 +7,6 @@ const colorHeader = config.ColorHeader !== undefined ? config.ColorHeader : true
let msgPrinted = false; let msgPrinted = false;
// 2. Toggle Function
// Hides the effect when a video player, trailer (in full width mode), dashboard, or user menu is active.
function togglePride() { function togglePride() {
const container = document.querySelector('.pride-container'); const container = document.querySelector('.pride-container');
if (!container) return; if (!container) return;
@@ -34,8 +31,6 @@ function togglePride() {
} }
} }
// 3. MutationObserver
// Watches the DOM for changes so the effect can auto-hide/show.
const observer = new MutationObserver(togglePride); const observer = new MutationObserver(togglePride);
observer.observe(document.body, { observer.observe(document.body, {
childList: true, childList: true,
@@ -43,8 +38,6 @@ observer.observe(document.body, {
attributes: true attributes: true
}); });
// 4. Element Creation
// Create and append your animated elements to the container.
function createElements() { function createElements() {
const container = document.querySelector('.pride-container') || document.createElement('div'); const container = document.querySelector('.pride-container') || document.createElement('div');
@@ -82,7 +75,6 @@ function createElements() {
} }
} }
// 5. Initialization
function initializePride() { function initializePride() {
if (!enabled) return; if (!enabled) return;
createElements(); createElements();

View File

@@ -16,9 +16,7 @@
z-index: 15; z-index: 15;
top: 0; top: 0;
translate: 0 -15vh; translate: 0 -15vh;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default;
cursor: default; cursor: default;
animation-name: resurrection-fall; animation-name: resurrection-fall;
animation-timing-function: linear; animation-timing-function: linear;

View File

@@ -238,7 +238,7 @@ function animateSanta() {
function startAnimation() { function startAnimation() {
const santaHeight = santa.offsetHeight; const santaHeight = santa.offsetHeight;
if (santaHeight === 0) { if (santaHeight === 0) {
setTimeout(startAnimation, 100); setTimeout(() => { if (document.body.contains(santa)) startAnimation(); }, 100);
return; return;
} }
// console.log('Santa height: ', santaHeight); // console.log('Santa height: ', santaHeight);
@@ -283,7 +283,7 @@ function animateSanta() {
animationFrameIdSanta = requestAnimationFrame(move); animationFrameIdSanta = requestAnimationFrame(move);
} else { } else {
const pause = Math.random() * ((maxSantaRestTime - minSantaRestTime) * 1000) + minSantaRestTime * 1000; const pause = Math.random() * ((maxSantaRestTime - minSantaRestTime) * 1000) + minSantaRestTime * 1000;
setTimeout(animateSanta, pause); setTimeout(() => { if (document.body.contains(santa)) animateSanta(); }, pause);
} }
} }

View File

@@ -21,40 +21,13 @@
color: #fff; color: #fff;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
text-shadow: 0 0 5px #000; text-shadow: 0 0 5px #000;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default;
-webkit-animation-name: heart-fall, heart-shake;
-webkit-animation-duration: 12s, 3s;
-webkit-animation-timing-function: linear, ease-in-out;
-webkit-animation-iteration-count: infinite, infinite;
animation-name: snowflakes-fall, snowflakes-shake;
cursor: default; cursor: default;
animation-name: snowflakes-fall, snowflakes-shake; animation-name: snowflakes-fall, snowflakes-shake;
animation-duration: 12s, 3s; animation-duration: 12s, 3s;
animation-timing-function: linear, ease-in-out; animation-timing-function: linear, ease-in-out;
animation-iteration-count: infinite, infinite; animation-iteration-count: infinite, infinite;
@-webkit-keyframes snowflakes-fall {
0% {
translate: 0 -10vh;
}
} }
100% {
translate: 0 110vh;
}
}
@-webkit-keyframes snowflakes-shake {
0%,
100% {
transform: translateX(0);
}
50% {
transform: translateX(80px);
}
}

View File

@@ -5,12 +5,13 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 9999; z-index: 10;
overflow: hidden; overflow: hidden;
contain: strict; contain: strict;
} }
.space-bg-glow { .space-bg-glow {
will-change: transform;
position: absolute; position: absolute;
top: 0; left: 0; width: 100vw; height: 100vh; top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 70% 30%, rgba(138, 43, 226, 0.15), transparent 60%), background: radial-gradient(circle at 70% 30%, rgba(138, 43, 226, 0.15), transparent 60%),
@@ -33,6 +34,7 @@
} }
.space-shooting-star { .space-shooting-star {
will-change: opacity;
position: absolute; position: absolute;
width: 250px; width: 250px;
height: 3px; height: 3px;
@@ -61,11 +63,11 @@
} }
.space-symbol img { .space-symbol img {
will-change: transform;
width: 6vh; width: 6vh;
height: auto; height: auto;
max-width: 60px; max-width: 60px;
object-fit: contain; object-fit: contain;
/* Add a slow spin to images */
animation: space-slow-spin var(--rot-dur, 20s) linear infinite; animation: space-slow-spin var(--rot-dur, 20s) linear infinite;
} }

View File

@@ -252,7 +252,8 @@ function createSpace() {
// Swap to a random image from the pool every time it completes an orbit (disappears) // Swap to a random image from the pool every time it completes an orbit (disappears)
if (imageArr.length > 1) { if (imageArr.length > 1) {
// The animation delay pushes the initial cycle, so we use setInterval matched to duration // The animation delay pushes the initial cycle, so we use setInterval matched to duration
setInterval(() => { const intervalId = setInterval(() => {
if (!document.body.contains(container)) { clearInterval(intervalId); return; }
// Update only if currently out of bounds to avoid popping // Update only if currently out of bounds to avoid popping
const rect = symbol.getBoundingClientRect(); const rect = symbol.getBoundingClientRect();
if (rect.right < 0 || rect.left > window.innerWidth) { if (rect.right < 0 || rect.left > window.innerWidth) {

View File

@@ -8,6 +8,7 @@
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
z-index: 10; z-index: 10;
contain: layout paint;
} }
.spooky { .spooky {
@@ -16,19 +17,8 @@
will-change: transform; will-change: transform;
translate: 0 120vh; translate: 0 120vh;
z-index: 15; z-index: 15;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
-webkit-user-select: none;
cursor: default; cursor: default;
-webkit-animation-name: spooky-float;
-webkit-animation-duration: 10s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
-webkit-animation-play-state: running;
animation-name: spooky-float; animation-name: spooky-float;
animation-duration: 10s; animation-duration: 10s;
animation-timing-function: linear; animation-timing-function: linear;
@@ -40,13 +30,6 @@
width: 30px; width: 30px;
height: auto; height: auto;
will-change: transform; will-change: transform;
-webkit-animation-name: spooky-shake;
-webkit-animation-duration: 3s;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-iteration-count: infinite;
-webkit-animation-play-state: running;
animation-name: spooky-shake; animation-name: spooky-shake;
animation-duration: 3s; animation-duration: 3s;
animation-timing-function: ease-in-out; animation-timing-function: ease-in-out;
@@ -59,22 +42,6 @@
width: 100%; width: 100%;
} }
@-webkit-keyframes spooky-float {
0% {
translate: 0 120vh;
opacity: 0;
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
translate: 0 -150px;
opacity: 0;
}
}
@keyframes spooky-float { @keyframes spooky-float {
0% { 0% {
@@ -93,14 +60,6 @@
} }
} }
@-webkit-keyframes spooky-shake {
0%, 100% {
transform: translateX(0) scale(1) rotate(15deg);
}
50% {
transform: translateX(80px) scale(1.2) rotate(-15deg);
}
}
@keyframes spooky-shake { @keyframes spooky-shake {
0%, 100% { 0%, 100% {
@@ -111,7 +70,6 @@
} }
} }
/* Base predefined starting offsets (if not overridden by js) */
.spooky:nth-of-type(0) { left: 1%; } .spooky:nth-of-type(0) { left: 1%; }
.spooky:nth-of-type(1) { left: 10%; } .spooky:nth-of-type(1) { left: 10%; }
.spooky:nth-of-type(2) { left: 20%; } .spooky:nth-of-type(2) { left: 20%; }

View File

@@ -5,16 +5,17 @@
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 9999; z-index: 10;
overflow: hidden; overflow: hidden;
contain: strict; contain: strict;
} }
.sports-symbol { .sports-symbol {
position: absolute; position: absolute;
top: -10vh; top: 0;
opacity: 0.9; opacity: 0.9;
z-index: 40; z-index: 40;
translate: 0 -10vh;
} }
.sports-inner { .sports-inner {
@@ -29,13 +30,15 @@
} }
.sports-confetti { .sports-confetti {
will-change: transform, opacity;
position: absolute; position: absolute;
top: -5vh; top: 0;
width: 10px; width: 10px;
height: 15px; height: 15px;
opacity: 0.8; opacity: 0.8;
animation: sports-confetti-fall linear infinite; animation: sports-confetti-fall linear infinite;
border-radius: 2px; border-radius: 2px;
translate: 0 -5vh;
} }
.sports-confetti.circle { .sports-confetti.circle {

View File

@@ -203,12 +203,12 @@ function createSports() {
}, arcDuration * 1000 + 500); }, arcDuration * 1000 + 500);
// Schedule the next trophy // Schedule the next trophy
setTimeout(launchTrophy, Math.random() * 20000 + 10000); // Wait 10-30s until next trophy setTimeout(() => { if (document.body.contains(container)) launchTrophy(); }, Math.random() * 20000 + 10000); // Wait 10-30s until next trophy
} }
// Launch initial trophy after a short delay // Launch initial trophy after a short delay
if (enableTrophy) { if (enableTrophy) {
setTimeout(launchTrophy, Math.random() * 5000 + 2000); setTimeout(() => { if (document.body.contains(container)) launchTrophy(); }, Math.random() * 5000 + 2000);
} }
// Add Germany Colored confetti (Black, Red, Gold) // Add Germany Colored confetti (Black, Red, Gold)

View File

@@ -29,13 +29,14 @@
/* Sunbeams */ /* Sunbeams */
.spring-sunbeam { .spring-sunbeam {
position: fixed; position: fixed;
top: -50%; top: 0;
height: 200%; height: 200%;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 200, 0.08) 50%, rgba(255, 255, 255, 0)); background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 200, 0.08) 50%, rgba(255, 255, 255, 0));
z-index: 5; z-index: 5;
transform-origin: top center; transform-origin: top center;
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
translate: 0 -50vh;
} }
/* Grass Container (Wrapper) */ /* Grass Container (Wrapper) */
@@ -72,6 +73,7 @@
/* SVG Meadow Layer */ /* SVG Meadow Layer */
.spring-meadow-layer { .spring-meadow-layer {
will-change: transform;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -94,6 +96,7 @@
} }
.spring-sway { .spring-sway {
will-change: transform;
transform-origin: bottom center; transform-origin: bottom center;
animation: spring-meadow-sway 4s ease-in-out infinite alternate; animation: spring-meadow-sway 4s ease-in-out infinite alternate;
} }

View File

@@ -295,7 +295,7 @@ function createBird(container) {
wrapper.addEventListener('animationend', (e) => { wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) { if (e.animationName.includes('fly-')) {
wrapper.remove(); wrapper.remove();
createBird(container); if (document.body.contains(container)) createBird(container);
} }
}); });
@@ -338,7 +338,7 @@ function createButterfly(container) {
wrapper.addEventListener('animationend', (e) => { wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) { if (e.animationName.includes('fly-')) {
wrapper.remove(); wrapper.remove();
createButterfly(container); if (document.body.contains(container)) createButterfly(container);
} }
}); });
@@ -385,7 +385,7 @@ function createBee(container) {
wrapper.addEventListener('animationend', (e) => { wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) { if (e.animationName.includes('fly-')) {
wrapper.remove(); wrapper.remove();
createBee(container); if (document.body.contains(container)) createBee(container);
} }
}); });
@@ -428,7 +428,7 @@ function createLadybugGif(container) {
wrapper.addEventListener('animationend', (e) => { wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('walk-')) { if (e.animationName.includes('walk-')) {
wrapper.remove(); wrapper.remove();
createLadybugGif(container); if (document.body.contains(container)) createLadybugGif(container);
} }
}); });

View File

@@ -13,10 +13,11 @@
.starwars-center { .starwars-center {
position: absolute; position: absolute;
top: 50%; top: 0;
left: 50%; left: 50%;
width: 0; width: 0;
height: 0; height: 0;
translate: 0 50vh;
} }
.starwars-streak { .starwars-streak {

View File

@@ -35,6 +35,7 @@
} }
.underwater-seaweed { .underwater-seaweed {
will-change: transform, opacity;
position: absolute; position: absolute;
bottom: -1vh; bottom: -1vh;
font-size: 4rem; font-size: 4rem;
@@ -52,6 +53,7 @@
} }
.underwater-bubble { .underwater-bubble {
will-change: transform;
position: absolute; position: absolute;
bottom: -5vh; bottom: -5vh;
border-radius: 50%; border-radius: 50%;
@@ -73,13 +75,13 @@
} }
@keyframes underwater-traverse-up { @keyframes underwater-traverse-up {
0% { top: 120vh; } 0% { top: 0; translate: 0 120vh; }
100% { top: -20vh; } 100% { top: 0; translate: 0 -20vh; }
} }
@keyframes underwater-traverse-down { @keyframes underwater-traverse-down {
0% { top: -20vh; } 0% { top: 0; translate: 0 -20vh; }
100% { top: 120vh; } 100% { top: 0; translate: 0 120vh; }
} }
@keyframes underwater-sway-y { @keyframes underwater-sway-y {
@@ -101,8 +103,9 @@
} }
.underwater-god-rays { .underwater-god-rays {
will-change: transform;
position: absolute; position: absolute;
top: -50vh; top: 0;
left: -50vw; left: -50vw;
width: 200vw; width: 200vw;
height: 200vh; height: 200vh;
@@ -119,6 +122,7 @@
transform-origin: top center; transform-origin: top center;
mix-blend-mode: overlay; mix-blend-mode: overlay;
filter: blur(5px); filter: blur(5px);
translate: 0 -50vh;
} }
@keyframes god-rays-sway { @keyframes god-rays-sway {