Refactor spring and carnival animations, enhance configuration options, and improve asset management

This commit is contained in:
CodeDevMLH
2026-02-21 04:31:15 +01:00
parent cde5201991
commit 4e8a37540f
6 changed files with 460 additions and 90 deletions

View File

@@ -18,7 +18,8 @@
will-change: top; will-change: top;
animation-name: carnival-fall; animation-name: carnival-fall;
animation-timing-function: linear; animation-timing-function: linear;
animation-iteration-count: infinite; animation-iteration-count: 1;
animation-fill-mode: forwards;
} }
.carnival-sway-wrapper { .carnival-sway-wrapper {
@@ -35,9 +36,8 @@
background-color: #f0f; background-color: #f0f;
will-change: transform; will-change: transform;
animation-name: carnival-flutter; animation-name: carnival-flutter;
animation-timing-function: ease-in-out; animation-timing-function: linear;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-duration: 2s;
} }
.carnival-confetti.circle { .carnival-confetti.circle {
@@ -52,15 +52,8 @@
} }
.carnival-confetti.triangle { .carnival-confetti.triangle {
width: 0;
height: 0;
background-color: transparent !important;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 10px solid;
width: 10px; width: 10px;
height: 10px; height: 10px;
background-color: inherit;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%); clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
} }
@@ -84,18 +77,9 @@
@keyframes carnival-flutter { @keyframes carnival-flutter {
0% { 0% {
transform: rotate3d(1, 1, 1, 0deg); transform: rotate3d(var(--rx, 1), var(--ry, 1), var(--rz, 0), 0deg);
}
25% {
transform: rotate3d(1, 0.5, 0, 90deg);
}
50% {
transform: rotate3d(0.5, 1, 0.5, 180deg);
}
75% {
transform: rotate3d(0, 0.5, 1, 270deg);
} }
100% { 100% {
transform: rotate3d(1, 1, 1, 360deg); transform: rotate3d(var(--rx, 1), var(--ry, 1), var(--rz, 0), var(--rot-dir, 360deg));
} }
} }

View File

@@ -1,11 +1,11 @@
const config = window.SeasonalsPluginConfig?.Carnival || {}; const config = window.SeasonalsPluginConfig?.Carnival || {};
const carnival = config.EnableCarnival !== undefined ? config.EnableCarnival : true; const carnival = config.EnableCarnival !== undefined ? config.EnableCarnival : true; // Enable/disable carnival
const randomCarnival = config.EnableRandomCarnival !== undefined ? config.EnableRandomCarnival : true; const randomCarnival = config.EnableRandomCarnival !== undefined ? config.EnableRandomCarnival : true; // Enable random carnival objects
const randomCarnivalMobile = config.EnableRandomCarnivalMobile !== undefined ? config.EnableRandomCarnivalMobile : false; const randomCarnivalMobile = config.EnableRandomCarnivalMobile !== undefined ? config.EnableRandomCarnivalMobile : false; // Enable random carnival objects on mobile
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; // Randomize falling and flutter speeds
const enableSway = config.EnableCarnivalSway !== undefined ? config.EnableCarnivalSway : true; const enableSway = config.EnableCarnivalSway !== undefined ? config.EnableCarnivalSway : true; // Enable side-to-side sway animation
const carnivalCount = config.ObjectCount || 80; const carnivalCount = config.ObjectCount || 120; // Number of confetti pieces to spawn
let msgPrinted = false; let msgPrinted = false;
@@ -86,14 +86,21 @@ function createConfettiPiece(container, isInitial = false) {
wrapper.style.left = `${Math.random() * 100}%`; wrapper.style.left = `${Math.random() * 100}%`;
// Random dimensions // Random dimensions
// MARK: CONFETTI SIZE (RECTANGLES)
if (!confetti.classList.contains('circle') && !confetti.classList.contains('square') && !confetti.classList.contains('triangle')) { if (!confetti.classList.contains('circle') && !confetti.classList.contains('square') && !confetti.classList.contains('triangle')) {
const width = Math.random() * 5 + 4; const width = Math.random() * 3 + 4; // 4-7px
const height = Math.random() * 6 + 8; const height = Math.random() * 5 + 8; // 8-13px
confetti.style.width = `${width}px`; confetti.style.width = `${width}px`;
confetti.style.height = `${height}px`; confetti.style.height = `${height}px`;
} else if (confetti.classList.contains('circle') || confetti.classList.contains('square')) {
// MARK: CONFETTI SIZE (CIRCLES/SQUARES)
const size = Math.random() * 5 + 5; // 5-10px
confetti.style.width = `${size}px`;
confetti.style.height = `${size}px`;
} }
// Animation settings // Animation settings
// MARK: CONFETTI FALLING SPEED (in seconds)
const duration = Math.random() * 5 + 5; const duration = Math.random() * 5 + 5;
let delay = 0; let delay = 0;
@@ -113,14 +120,22 @@ function createConfettiPiece(container, isInitial = false) {
swayWrapper.style.animationDelay = `-${Math.random() * 5}s`; swayWrapper.style.animationDelay = `-${Math.random() * 5}s`;
// Random sway amplitude (using CSS variable for dynamic keyframe) // Random sway amplitude (using CSS variable for dynamic keyframe)
// Sway between 30px and 100px // MARK: SWAY DISTANCE RANGE (in px)
const swayAmount = Math.random() * 70 + 30; const swayAmount = Math.random() * 70 + 30; // 30-100px
const direction = Math.random() > 0.5 ? 1 : -1; const direction = Math.random() > 0.5 ? 1 : -1;
swayWrapper.style.setProperty('--sway-amount', `${swayAmount * direction}px`); swayWrapper.style.setProperty('--sway-amount', `${swayAmount * direction}px`);
} }
// Flutter speed variation // Flutter speed and random 3D rotation axis
// MARK: CONFETTI FLUTTER ROTATION SPEED
confetti.style.animationDuration = `${Math.random() * 2 + 1}s`; confetti.style.animationDuration = `${Math.random() * 2 + 1}s`;
confetti.style.setProperty('--rx', Math.random().toFixed(2));
confetti.style.setProperty('--ry', Math.random().toFixed(2));
confetti.style.setProperty('--rz', (Math.random() * 0.5).toFixed(2));
// Random direction for 3D rotation
const rotDir = Math.random() > 0.5 ? 1 : -1;
confetti.style.setProperty('--rot-dir', `${rotDir * 360}deg`);
if (enableSway) { if (enableSway) {
swayWrapper.appendChild(confetti); swayWrapper.appendChild(confetti);
@@ -129,6 +144,14 @@ function createConfettiPiece(container, isInitial = false) {
wrapper.appendChild(confetti); wrapper.appendChild(confetti);
} }
// Respawn confetti when it hits the bottom
wrapper.addEventListener('animationend', (e) => {
if (e.animationName === 'carnival-fall') {
wrapper.remove();
createConfettiPiece(container, false); // respawn without initial huge delay
}
});
container.appendChild(wrapper); container.appendChild(wrapper);
} }
@@ -154,7 +177,7 @@ function initCarnivalObjects() {
} }
// Initial confetti // Initial confetti
for (let i = 0; i < 30; i++) { for (let i = 0; i < 60; i++) {
createConfettiPiece(container, true); createConfettiPiece(container, true);
} }
} }

View File

@@ -73,6 +73,11 @@ const ThemeConfigs = {
js: '../Seasonals/Resources/carnival.js', js: '../Seasonals/Resources/carnival.js',
containerClass: 'carnival-container' containerClass: 'carnival-container'
}, },
cherryblossom: {
css: '../Seasonals/Resources/cherryblossom.css',
js: '../Seasonals/Resources/cherryblossom.js',
containerClass: 'cherryblossom-container'
},
none: { none: {
containerClass: 'none' containerClass: 'none'
}, },
@@ -246,6 +251,12 @@ const SeasonalsManager = {
if (response.ok) { if (response.ok) {
this.config = await response.json(); this.config = await response.json();
window.SeasonalsPluginConfig = this.config; window.SeasonalsPluginConfig = this.config;
if (this.config.IsEnabled === false) {
console.log('Seasonals: Plugin is disabled globally.');
return;
}
console.log('Seasonals: Seasonals Config loaded:', this.config); console.log('Seasonals: Seasonals Config loaded:', this.config);
} }
} catch (error) { } catch (error) {

View File

@@ -5,7 +5,7 @@
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100vh;
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 1000;
} }
@@ -34,21 +34,16 @@
z-index: 5; z-index: 5;
transform-origin: top center; transform-origin: top center;
pointer-events: none; pointer-events: none;
opacity: 0;
animation-name: spring-beam-pulse;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
} }
/* Grass */ /* Grass */
.spring-grass-container { .spring-grass-container {
position: fixed; position: absolute;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 80px; height: 80px;
z-index: 1002;
overflow: hidden;
pointer-events: none; pointer-events: none;
} }
@@ -66,6 +61,73 @@
animation-iteration-count: infinite; animation-iteration-count: infinite;
} }
/* Birds */
.spring-bird {
position: static !important;
display: block;
z-index: 1001;
/* MARK: BIRD SIZE */
width: 80px;
height: auto;
will-change: transform;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
/* Butterflies */
.spring-butterfly {
position: static !important;
display: block;
z-index: 1001;
/* MARK: BUTTERFLY SIZE */
width: 40px;
height: auto;
will-change: transform;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
/* Bee */
.spring-bee {
position: static !important;
display: block;
z-index: 1001;
/* MARK: BEE SIZE */
width: 30px;
height: auto;
will-change: transform;
}
/* Ladybug */
.spring-ladybug-gif {
position: static !important;
display: block;
/* MARK: LADYBUG SIZE */
width: 30px;
height: auto;
will-change: transform;
animation-timing-function: linear;
animation-iteration-count: infinite;
--bug-rotation: -55deg;
}
.spring-ladybug-wrapper {
z-index: 1002;
}
/* Generic Wrappers */
.spring-anim-wrapper {
position: fixed;
z-index: 1001;
will-change: transform;
top: 0; left: 0;
}
.spring-mirror-wrapper {
width: 100%; height: 100%;
will-change: transform;
transform-origin: center;
}
@keyframes spring-float { @keyframes spring-float {
@@ -77,9 +139,9 @@
} }
@keyframes spring-beam-pulse { @keyframes spring-beam-pulse {
0% { opacity: 0.3; transform: rotate(45deg) scaleX(1); } 0% { opacity: 0; transform: rotate(var(--beam-rotation, 45deg)) scaleX(0.8); }
50% { opacity: 0.6; transform: rotate(45deg) scaleX(1.1); } 50% { opacity: 0.6; transform: rotate(var(--beam-rotation, 45deg)) scaleX(1.2); }
100% { opacity: 0.3; transform: rotate(45deg) scaleX(1); } 100% { opacity: 0; transform: rotate(var(--beam-rotation, 45deg)) scaleX(0.8); }
} }
@keyframes spring-grass-sway { @keyframes spring-grass-sway {
@@ -88,4 +150,63 @@
100% { transform: rotate(0deg); } 100% { transform: rotate(0deg); }
} }
/* Wrapper animations (Flight across screen) */
@keyframes spring-fly-right-wrapper {
0% { transform: translateX(-10vw); }
100% { transform: translateX(110vw); }
}
@keyframes spring-fly-left-wrapper {
0% { transform: translateX(110vw); }
100% { transform: translateX(-10vw); }
}
/* Vertical Drift for Sloped Flight */
@keyframes spring-vertical-drift {
0% { top: var(--start-y, 10%); }
100% { top: var(--end-y, 10%); }
}
/* Inner animations (Bobbing/Fluttering) */
@keyframes spring-bob {
0% { transform: translateY(0); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0); }
}
@keyframes spring-flutter {
0% { transform: translateY(0) rotate(0deg); }
25% { transform: translateY(-5px) rotate(5deg); }
50% { transform: translateY(0) rotate(0deg); }
75% { transform: translateY(5px) rotate(-5deg); }
100% { transform: translateY(0) rotate(0deg); }
}
/* Bee Buzz - Reduced Intensity */
@keyframes spring-buzz {
0% { transform: translate(0, 0); }
25% { transform: translate(2px, -2px); }
50% { transform: translate(0, 2px); }
75% { transform: translate(-2px, -2px); }
100% { transform: translate(0, 0); }
}
/* Ladybug Walk (Wrapper handles X) */
@keyframes spring-walk-right {
0% { transform: translateX(-10vw); }
100% { transform: translateX(110vw); }
}
@keyframes spring-walk-left {
0% { transform: translateX(110vw); }
100% { transform: translateX(-10vw); }
}
/* Ladybug Crawl (Inner Wobble) */
@keyframes spring-crawl {
0% { transform: translateY(0) rotate(var(--bug-rotation, 0deg)); }
25% { transform: translateY(-3px) rotate(calc(var(--bug-rotation, 0deg) + 8deg)); }
50% { transform: translateY(0) rotate(var(--bug-rotation, 0deg)); }
75% { transform: translateY(-3px) rotate(calc(var(--bug-rotation, 0deg) - 8deg)); }
100% { transform: translateY(0) rotate(var(--bug-rotation, 0deg)); }
}

View File

@@ -1,13 +1,28 @@
const config = window.SeasonalsPluginConfig?.Spring || {}; const config = window.SeasonalsPluginConfig?.Spring || {};
const spring = config.EnableSpring !== undefined ? config.EnableSpring : true; const spring = config.EnableSpring !== undefined ? config.EnableSpring : true; // Enable/disable spring
const pollenCount = config.PollenCount || 15; const pollenCount = config.PollenCount || 30; // Number of pollen particles
const sunbeamCount = config.SunbeamCount || 5; const sunbeamCount = config.SunbeamCount || 5; // Number of sunbeams
const enableSunbeams = config.EnableSpringSunbeams !== undefined ? config.EnableSpringSunbeams : true; // Enable/disable sunbeams
const birdCount = config.BirdCount !== undefined ? config.BirdCount : 3; // Number of birds
const butterflyCount = config.ButterflyCount !== undefined ? config.ButterflyCount : 4; // Number of butterflies
const beeCount = config.BeeCount !== undefined ? config.BeeCount : 1; // Number of bees
const ladybugCount = config.LadybugCount !== undefined ? config.LadybugCount : 1; // Number of ladybugs
const randomSpring = config.EnableRandomSpring !== undefined ? config.EnableRandomSpring : true; // Enable random spring objects
const randomSpring = config.EnableRandomSpring !== undefined ? config.EnableRandomSpring : true; const birdImages = [
const randomSpringMobile = config.EnableRandomSpringMobile !== undefined ? config.EnableRandomSpringMobile : false; '../Seasonals/Resources/spring_assets/Bird_1.gif',
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; '../Seasonals/Resources/spring_assets/Bird_2.gif',
const enableSunbeams = config.EnableSpringSunbeams !== undefined ? config.EnableSpringSunbeams : true; '../Seasonals/Resources/spring_assets/Bird_3.gif'
];
const butterflyImages = [
'../Seasonals/Resources/spring_assets/Butterfly_1.gif',
'../Seasonals/Resources/spring_assets/Butterfly_2.gif'
];
const beeImage = '../Seasonals/Resources/spring_assets/Bee.gif';
const ladybugImage = '../Seasonals/Resources/spring_assets/ladybug.gif';
let msgPrinted = false; let msgPrinted = false;
@@ -43,11 +58,13 @@ function createPollen(container) {
const pollen = document.createElement('div'); const pollen = document.createElement('div');
pollen.classList.add('spring-pollen'); pollen.classList.add('spring-pollen');
// MARK: POLLEN START VERTICAL POSITION (in %)
const startY = Math.random() * 60 + 20; const startY = Math.random() * 60 + 20;
pollen.style.top = `${startY}%`; pollen.style.top = `${startY}%`;
pollen.style.left = `${Math.random() * 100}%`; pollen.style.left = `${Math.random() * 100}%`;
const size = Math.random() * 3 + 1; // MARK: POLLEN SIZE
const size = Math.random() * 3 + 1; // 1-4px
pollen.style.width = `${size}px`; pollen.style.width = `${size}px`;
pollen.style.height = `${size}px`; pollen.style.height = `${size}px`;
@@ -58,27 +75,40 @@ function createPollen(container) {
container.appendChild(pollen); container.appendChild(pollen);
} }
function createSunbeam(container) { function spawnSunbeamGroup(container, count) {
if (!enableSunbeams) return; if (!enableSunbeams) return;
const rotate = Math.random() * 30 - 15 + 45;
let beamsActive = count;
for (let i = 0; i < count; i++) {
const beam = document.createElement('div'); const beam = document.createElement('div');
beam.classList.add('spring-sunbeam'); beam.classList.add('spring-sunbeam');
const left = Math.random() * 100; // Spread across full width const left = Math.random() * 100;
beam.style.left = `${left}%`; beam.style.left = `${left}%`;
// Thinner beams as requested // MARK: SUNBEAM WIDTH (in px)
const width = Math.random() * 20 + 10; // 10-30px wide const width = Math.random() * 5 + 15; // 5-20px wide
beam.style.width = `${width}px`; beam.style.width = `${width}px`;
const rotate = Math.random() * 20 - 10 + 45; beam.style.setProperty('--beam-rotation', `${rotate}deg`);
beam.style.transform = `rotate(${rotate}deg)`;
const duration = Math.random() * 10 + 10; const duration = Math.random() * 7 + 8; // 8-15s
beam.style.animationDuration = `${duration}s`; beam.style.animation = `spring-beam-pulse ${duration}s ease-in-out forwards`;
beam.style.animationDelay = `-${Math.random() * 10}s`;
beam.style.animationDelay = `${Math.random() * 3}s`;
beam.addEventListener('animationend', () => {
beam.remove();
beamsActive--;
if (beamsActive === 0) {
spawnSunbeamGroup(container, count);
}
});
container.appendChild(beam); container.appendChild(beam);
}
} }
function createGrass(container) { function createGrass(container) {
@@ -89,15 +119,14 @@ function createGrass(container) {
container.appendChild(grassContainer); container.appendChild(grassContainer);
} }
// Clear existing grass if any (for resize)
grassContainer.innerHTML = ''; grassContainer.innerHTML = '';
// More grass: 1 blade every 3px (was 15px)
const bladeCount = window.innerWidth / 3; const bladeCount = window.innerWidth / 3;
for (let i = 0; i < bladeCount; i++) { for (let i = 0; i < bladeCount; i++) {
const blade = document.createElement('div'); const blade = document.createElement('div');
blade.classList.add('spring-grass'); blade.classList.add('spring-grass');
// MARK: GRASS HEIGHT
const height = Math.random() * 40 + 20; // 20-60px height const height = Math.random() * 40 + 20; // 20-60px height
blade.style.height = `${height}px`; blade.style.height = `${height}px`;
blade.style.left = `${i * 3 + Math.random() * 2}px`; blade.style.left = `${i * 3 + Math.random() * 2}px`;
@@ -109,6 +138,11 @@ function createGrass(container) {
const hue = 100 + Math.random() * 40; const hue = 100 + Math.random() * 40;
blade.style.backgroundColor = `hsl(${hue}, 60%, 40%)`; blade.style.backgroundColor = `hsl(${hue}, 60%, 40%)`;
// Random z-index to interleave with Ladybug (1002)
// Values: 1001 (behind) or 1003 (front)
const z = Math.random() > 0.5 ? 1001 : 1003;
blade.style.zIndex = z;
grassContainer.appendChild(blade); grassContainer.appendChild(blade);
} }
} }
@@ -125,25 +159,219 @@ function initSpringObjects() {
createGrass(container); createGrass(container);
if (enableSunbeams) { if (enableSunbeams) {
// Initial sunbeams spawnSunbeamGroup(container, sunbeamCount);
for (let i = 0; i < sunbeamCount; i++) {
createSunbeam(container);
}
} }
} }
function initializeSpring() { function initializeSpring() {
if (!spring) return; if (!spring) {
console.warn('Spring is disabled.');
return;
}
initSpringObjects(); initSpringObjects();
toggleSpring(); toggleSpring();
const screenWidth = window.innerWidth; const container = document.querySelector('.spring-container');
if (randomSpring && (screenWidth > 768 || randomSpringMobile)) { if (container) {
addRandomSpringObjects(); if (randomSpring) {
// Add Pollen
for (let i = 0; i < pollenCount; i++) {
createPollen(container);
}
// Add Birds
for (let i = 0; i < birdCount; i++) {
setTimeout(() => createBird(container), Math.random() * 1000); // 0-1s desync
}
// Add Butterflies
for (let i = 0; i < butterflyCount; i++) {
setTimeout(() => createButterfly(container), Math.random() * 1000); // 0-1s desync
}
// Add Bees
for (let i = 0; i < beeCount; i++) {
setTimeout(() => createBee(container), Math.random() * 1000); // 0-1s desync
}
// Add Ladybugs
for (let i = 0; i < ladybugCount; i++) {
setTimeout(() => createLadybugGif(container), Math.random() * 1000); // 0-1s desync
}
}
} }
} }
// Debounce helper function createBird(container) {
const wrapper = document.createElement('div');
wrapper.classList.add('spring-anim-wrapper');
wrapper.classList.add('spring-bird-wrapper');
const mirror = document.createElement('div');
mirror.classList.add('spring-mirror-wrapper');
const bird = document.createElement('img');
bird.classList.add('spring-bird');
const randomSrc = birdImages[Math.floor(Math.random() * birdImages.length)];
bird.src = randomSrc;
const direction = Math.random() > 0.5 ? 'right' : 'left';
// MARK: BIRD SPEED (10-15s)
const duration = Math.random() * 5 + 10;
// MARK: BIRD HEIGHT RANGE (in %)
const startY = Math.random() * 55 + 5; // Start 5-60%
const endY = Math.random() * 55 + 5; // End 5-60%
wrapper.style.setProperty('--start-y', `${startY}%`);
wrapper.style.setProperty('--end-y', `${endY}%`);
if (direction === 'right') {
wrapper.style.animation = `spring-fly-right-wrapper ${duration}s linear forwards, spring-vertical-drift ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(-1)';
} else {
wrapper.style.animation = `spring-fly-left-wrapper ${duration}s linear forwards, spring-vertical-drift ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(1)';
}
wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) {
wrapper.remove();
createBird(container);
}
});
bird.style.animation = `spring-bob 2s ease-in-out infinite`;
wrapper.style.top = `${startY}%`;
mirror.appendChild(bird);
wrapper.appendChild(mirror);
container.appendChild(wrapper);
}
function createButterfly(container) {
const wrapper = document.createElement('div');
wrapper.classList.add('spring-anim-wrapper');
wrapper.classList.add('spring-butterfly-wrapper');
const mirror = document.createElement('div');
mirror.classList.add('spring-mirror-wrapper');
const butterfly = document.createElement('img');
butterfly.classList.add('spring-butterfly');
const randomSrc = butterflyImages[Math.floor(Math.random() * butterflyImages.length)];
butterfly.src = randomSrc;
const duration = Math.random() * 15 + 25;
const direction = Math.random() > 0.5 ? 'right' : 'left';
if (direction === 'right') {
wrapper.style.animation = `spring-fly-right-wrapper ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(1)';
} else {
wrapper.style.animation = `spring-fly-left-wrapper ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(-1)';
}
wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) {
wrapper.remove();
createButterfly(container);
}
});
// MARK: BUTTERFLY FLUTTER RHYTHM
butterfly.style.animation = `spring-flutter 3s ease-in-out infinite`;
butterfly.style.animationDelay = `-${Math.random() * 3}s`;
// MARK: BUTTERFLY HEIGHT (in %)
const top = Math.random() * 35 + 50; // 50-85%
wrapper.style.top = `${top}%`;
mirror.appendChild(butterfly);
wrapper.appendChild(mirror);
container.appendChild(wrapper);
}
function createBee(container) {
const wrapper = document.createElement('div');
wrapper.classList.add('spring-anim-wrapper');
wrapper.classList.add('spring-bee-wrapper');
const mirror = document.createElement('div');
mirror.classList.add('spring-mirror-wrapper');
const bee = document.createElement('img');
bee.classList.add('spring-bee');
bee.src = beeImage;
const duration = Math.random() * 10 + 15;
const direction = Math.random() > 0.5 ? 'right' : 'left';
if (direction === 'right') {
wrapper.style.animation = `spring-fly-right-wrapper ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(1)';
} else {
wrapper.style.animation = `spring-fly-left-wrapper ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(-1)';
}
wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('fly-')) {
wrapper.remove();
createBee(container);
}
});
// MARK: BEE HEIGHT (in %)
const top = Math.random() * 60 + 20; // 20-80%
wrapper.style.top = `${top}%`;
mirror.appendChild(bee);
wrapper.appendChild(mirror);
container.appendChild(wrapper);
}
function createLadybugGif(container) {
const wrapper = document.createElement('div');
wrapper.classList.add('spring-anim-wrapper');
wrapper.classList.add('spring-ladybug-wrapper');
const mirror = document.createElement('div');
mirror.classList.add('spring-mirror-wrapper');
const bug = document.createElement('img');
bug.classList.add('spring-ladybug-gif');
bug.src = ladybugImage;
const direction = Math.random() > 0.5 ? 'right' : 'left';
const duration = Math.random() * 20 + 30;
if (direction === 'right') {
wrapper.style.animation = `spring-walk-right ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(1)';
} else {
wrapper.style.animation = `spring-walk-left ${duration}s linear forwards`;
mirror.style.transform = 'scaleX(-1)';
}
wrapper.addEventListener('animationend', (e) => {
if (e.animationName.includes('walk-')) {
wrapper.remove();
createLadybugGif(container);
}
});
bug.style.animation = `spring-crawl 2s ease-in-out infinite`;
wrapper.style.top = 'auto';
wrapper.style.bottom = '5px';
mirror.appendChild(bug);
wrapper.appendChild(mirror);
container.appendChild(wrapper);
}
function debounce(func, wait) { function debounce(func, wait) {
let timeout; let timeout;
return function executedFunction(...args) { return function executedFunction(...args) {
@@ -156,7 +384,6 @@ function debounce(func, wait) {
}; };
} }
// Resize handler for grass
const handleResize = debounce(() => { const handleResize = debounce(() => {
const container = document.querySelector('.spring-container'); const container = document.querySelector('.spring-container');
if (container) { if (container) {

View File

@@ -1,11 +1,11 @@
const config = window.SeasonalsPluginConfig?.Summer || {}; const config = window.SeasonalsPluginConfig?.Summer || {};
const summer = config.EnableSummer !== undefined ? config.EnableSummer : true; // enable/disable summer const summer = config.EnableSummer !== undefined ? config.EnableSummer : true; // Enable/disable summer theme
const bubbleCount = config.BubbleCount || 20; const bubbleCount = config.BubbleCount || 30; // Number of bubbles
const dustCount = config.DustCount || 50; const dustCount = config.DustCount || 50; // Number of dust particles
const randomSummer = config.EnableRandomSummer !== undefined ? config.EnableRandomSummer : true; // enable random objects const randomSummer = config.EnableRandomSummer !== undefined ? config.EnableRandomSummer : true; // Enable random generating objects
const randomSummerMobile = config.EnableRandomSummerMobile !== undefined ? config.EnableRandomSummerMobile : false; // enable random objects on mobile const randomSummerMobile = config.EnableRandomSummerMobile !== undefined ? config.EnableRandomSummerMobile : false; // Enable random generating objects on mobile
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; // enable different animation duration const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true; // Randomize animation duration of bubbles and dust
let msgPrinted = false; let msgPrinted = false;
@@ -51,10 +51,12 @@ function createBubble(container, isDust = false) {
// Random size // Random size
if (!isDust) { if (!isDust) {
// MARK: BUBBLE SIZE
const size = Math.random() * 20 + 10; // 10-30px bubbles const size = Math.random() * 20 + 10; // 10-30px bubbles
bubble.style.width = `${size}px`; bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`; bubble.style.height = `${size}px`;
} else { } else {
// MARK: DUST SIZE
const size = Math.random() * 3 + 1; // 1-4px dust const size = Math.random() * 3 + 1; // 1-4px dust
bubble.style.width = `${size}px`; bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`; bubble.style.height = `${size}px`;
@@ -81,7 +83,7 @@ function addRandomSummerObjects() {
createBubble(container, false); createBubble(container, false);
} }
// Add some dust particles (more of them, they are subtle) // Add some dust particles
for (let i = 0; i < dustCount; i++) { for (let i = 0; i < dustCount; i++) {
createBubble(container, true); createBubble(container, true);
} }
@@ -110,10 +112,12 @@ function initSummerObjects() {
bubble.style.left = `${randomLeft}%`; bubble.style.left = `${randomLeft}%`;
if (!isDust) { if (!isDust) {
// MARK: BUBBLE SIZE
const size = Math.random() * 20 + 10; const size = Math.random() * 20 + 10;
bubble.style.width = `${size}px`; bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`; bubble.style.height = `${size}px`;
} else { } else {
// MARK: DUST SIZE
const size = Math.random() * 3 + 1; const size = Math.random() * 3 + 1;
bubble.style.width = `${size}px`; bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`; bubble.style.height = `${size}px`;