Refactor birthday, olympia, space, and sports animations; enhance symbol management and visual effects
- Updated birthday.js to improve balloon visuals and confetti effects, including new color schemes and enhanced pop animations. - Removed legacy fallback logic in olympia.js and improved image error handling. - Enhanced space.js with configurable counts for planets, astronauts, satellites, ISS, and rockets; improved shooting star animations and added random image swapping. - Simplified sports.js by removing legacy emoji fallback logic and optimizing ball creation based on selected categories. - Adjusted CSS styles across birthday, olympia, space, and sports for better visual consistency and performance. [skip ci]
This commit is contained in:
@@ -389,6 +389,7 @@ public class SportsOptions
|
|||||||
public bool EnableDifferentDuration { get; set; } = true;
|
public bool EnableDifferentDuration { get; set; } = true;
|
||||||
public string TurfColor { get; set; } = "#228b22";
|
public string TurfColor { get; set; } = "#228b22";
|
||||||
public string SportsBalls { get; set; } = "football,basketball,tennis,volleyball";
|
public string SportsBalls { get; set; } = "football,basketball,tennis,volleyball";
|
||||||
|
public bool EnableTrophy { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OlympiaOptions
|
public class OlympiaOptions
|
||||||
@@ -402,7 +403,11 @@ public class OlympiaOptions
|
|||||||
|
|
||||||
public class SpaceOptions
|
public class SpaceOptions
|
||||||
{
|
{
|
||||||
public int SymbolCount { get; set; } = 25;
|
public int PlanetCount { get; set; } = 12;
|
||||||
|
public int AstronautCount { get; set; } = 5;
|
||||||
|
public int SatelliteCount { get; set; } = 2;
|
||||||
|
public int IssCount { get; set; } = 1;
|
||||||
|
public int RocketCount { get; set; } = 1;
|
||||||
public bool EnableSpace { get; set; } = true;
|
public bool EnableSpace { get; set; } = true;
|
||||||
public bool EnableRandomSymbols { get; set; } = true;
|
public bool EnableRandomSymbols { get; set; } = true;
|
||||||
public bool EnableRandomSymbolsMobile { get; set; } = false;
|
public bool EnableRandomSymbolsMobile { get; set; } = false;
|
||||||
@@ -429,7 +434,8 @@ public class UnderwaterOptions
|
|||||||
|
|
||||||
public class BirthdayOptions
|
public class BirthdayOptions
|
||||||
{
|
{
|
||||||
public int SymbolCount { get; set; } = 25;
|
public int SymbolCount { get; set; } = 5;
|
||||||
|
public int ConfettiCount { get; set; } = 60;
|
||||||
public bool EnableBirthday { get; set; } = true;
|
public bool EnableBirthday { get; set; } = true;
|
||||||
public bool EnableRandomSymbols { get; set; } = true;
|
public bool EnableRandomSymbols { get; set; } = true;
|
||||||
public bool EnableRandomSymbolsMobile { get; set; } = false;
|
public bool EnableRandomSymbolsMobile { get; set; } = false;
|
||||||
|
|||||||
@@ -70,16 +70,13 @@
|
|||||||
<option value="carnival">Carnival (Confetti)</option>
|
<option value="carnival">Carnival (Confetti)</option>
|
||||||
<option value="cherryblossom">Cherry Blossom</option>
|
<option value="cherryblossom">Cherry Blossom</option>
|
||||||
<option value="resurrection">Resurrection by Bioflash257</option>
|
<option value="resurrection">Resurrection by Bioflash257</option>
|
||||||
<option value="championships" disabled>European/World Championships (not implemented yet. Please commit ideas/implementation in a issue or PR)</option>
|
|
||||||
<option value="patrick" disabled>St. Patrick's Day (not implemented yet. Please commit ideas/implementation in a issue or PR)</option>
|
|
||||||
<option value="thanksgiving" disabled>Thanksgiving (not implemented yet. Please commit ideas/implementation in a issue or PR)</option>
|
|
||||||
<option value="earthday">Earth Day (Growing Vines)</option>
|
<option value="earthday">Earth Day (Growing Vines)</option>
|
||||||
<option value="eurovision">Eurovision (Dancing Notes)</option>
|
<option value="eurovision">Eurovision (Dancing Notes)</option>
|
||||||
<option value="oscar">Oscar Awards (Glamour & Flashes)</option>
|
<option value="oscar">Oscar Awards (Glamour & Flashes)</option>
|
||||||
<option value="matrix">Matrix</option>
|
<option value="matrix">Matrix</option>
|
||||||
<option value="pride">Pride (Rainbow Border)</option>
|
<option value="pride">Pride (Rainbow Border)</option>
|
||||||
<option value="rain">Rain (Pure Rain)</option>
|
<option value="rain">Rain (Pure Rain)</option>
|
||||||
<option value="storm">Storm (Heavy Rain & Lightning (⚠️Epilepsy Warning))</option>
|
<option value="storm">Storm (Heavy Rain & Lightning (⚠️Epilepsy Warning⚠️))</option>
|
||||||
<option value="frost">Frost / Ice</option>
|
<option value="frost">Frost / Ice</option>
|
||||||
<option value="filmnoir">Film-Noir (Classic B&W Cinema)</option>
|
<option value="filmnoir">Film-Noir (Classic B&W Cinema)</option>
|
||||||
<option value="marioday">Mario Day (March 10)</option>
|
<option value="marioday">Mario Day (March 10)</option>
|
||||||
@@ -88,6 +85,8 @@
|
|||||||
<option value="friday13">Friday the 13th</option>
|
<option value="friday13">Friday the 13th</option>
|
||||||
<option value="eidalfitr">Eid al-Fitr (Sugar Feast)</option>
|
<option value="eidalfitr">Eid al-Fitr (Sugar Feast)</option>
|
||||||
<option value="spooky">Spooky</option>
|
<option value="spooky">Spooky</option>
|
||||||
|
<option value="patrick" disabled>St. Patrick's Day (not implemented yet. Please commit ideas/implementation in a issue or PR)</option>
|
||||||
|
<option value="thanksgiving" disabled>Thanksgiving (not implemented yet. Please commit ideas/implementation in a issue or PR)</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="fieldDescription">The season to display if automation is disabled or no "Auto Selection" rule matches the current date.</div>
|
<div class="fieldDescription">The season to display if automation is disabled or no "Auto Selection" rule matches the current date.</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -935,6 +934,13 @@
|
|||||||
<span>Enable Different Duration</span>
|
<span>Enable Different Duration</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
|
<label class="emby-checkbox-label">
|
||||||
|
<input id="EnableTrophy" name="EnableTrophy" type="checkbox" is="emby-checkbox" />
|
||||||
|
<span>Enable Trophy</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription">Enable the flying trophy animation.</div>
|
||||||
|
</div>
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<label class="inputLabel" for="TurfColor">Turf Color</label>
|
<label class="inputLabel" for="TurfColor">Turf Color</label>
|
||||||
<input is="emby-input" type="color" id="TurfColor" name="TurfColor" value="#228b22" />
|
<input is="emby-input" type="color" id="TurfColor" name="TurfColor" value="#228b22" />
|
||||||
@@ -1054,9 +1060,29 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<label class="inputLabel" for="SpaceSymbolCount">Symbol Count</label>
|
<label class="inputLabel" for="PlanetCount">Planet Count</label>
|
||||||
<input is="emby-input" type="number" id="SpaceSymbolCount" name="SpaceSymbolCount" />
|
<input is="emby-input" type="number" id="PlanetCount" name="PlanetCount" />
|
||||||
<div class="fieldDescription">Number of additional symbols displayed (if enabled).</div>
|
<div class="fieldDescription">Number of planets displayed (if enabled).</div>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel" for="AstronautCount">Astronaut Count</label>
|
||||||
|
<input is="emby-input" type="number" id="AstronautCount" name="AstronautCount" />
|
||||||
|
<div class="fieldDescription">Number of astronauts displayed (if enabled).</div>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel" for="SatelliteCount">Satellite Count</label>
|
||||||
|
<input is="emby-input" type="number" id="SatelliteCount" name="SatelliteCount" />
|
||||||
|
<div class="fieldDescription">Number of satellites displayed (if enabled).</div>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel" for="IssCount">ISS Count</label>
|
||||||
|
<input is="emby-input" type="number" id="IssCount" name="IssCount" />
|
||||||
|
<div class="fieldDescription">Number of ISS symbols displayed (if enabled).</div>
|
||||||
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel" for="RocketCount">Rocket Count</label>
|
||||||
|
<input is="emby-input" type="number" id="RocketCount" name="RocketCount" />
|
||||||
|
<div class="fieldDescription">Number of rockets displayed (if enabled).</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label class="emby-checkbox-label">
|
<label class="emby-checkbox-label">
|
||||||
@@ -1171,6 +1197,11 @@
|
|||||||
<input is="emby-input" type="number" id="BirthdaySymbolCount" name="BirthdaySymbolCount" />
|
<input is="emby-input" type="number" id="BirthdaySymbolCount" name="BirthdaySymbolCount" />
|
||||||
<div class="fieldDescription">Number of additional symbols displayed (if enabled).</div>
|
<div class="fieldDescription">Number of additional symbols displayed (if enabled).</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="inputContainer">
|
||||||
|
<label class="inputLabel" for="BirthdayConfettiCount">Confetti Count</label>
|
||||||
|
<input is="emby-input" type="number" id="BirthdayConfettiCount" name="BirthdayConfettiCount" />
|
||||||
|
<div class="fieldDescription">Number of confetti pieces created when a balloon bursts.</div>
|
||||||
|
</div>
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label class="emby-checkbox-label">
|
<label class="emby-checkbox-label">
|
||||||
<input id="EnableDifferentDurationBirthday" name="EnableDifferentDurationBirthday" type="checkbox" is="emby-checkbox" />
|
<input id="EnableDifferentDurationBirthday" name="EnableDifferentDurationBirthday" type="checkbox" is="emby-checkbox" />
|
||||||
@@ -1810,7 +1841,11 @@
|
|||||||
document.querySelector('#EnableRandomSymbolsSpace').checked = config.Space.EnableRandomSymbols || false;
|
document.querySelector('#EnableRandomSymbolsSpace').checked = config.Space.EnableRandomSymbols || false;
|
||||||
document.querySelector('#EnableRandomSymbolsMobileSpace').checked = config.Space.EnableRandomSymbolsMobile || false;
|
document.querySelector('#EnableRandomSymbolsMobileSpace').checked = config.Space.EnableRandomSymbolsMobile || false;
|
||||||
document.querySelector('#EnableDifferentDurationSpace').checked = config.Space.EnableDifferentDuration || false;
|
document.querySelector('#EnableDifferentDurationSpace').checked = config.Space.EnableDifferentDuration || false;
|
||||||
document.querySelector('#SpaceSymbolCount').value = config.Space.SymbolCount || 25;
|
document.querySelector('#PlanetCount').value = config.Space.PlanetCount !== undefined ? config.Space.PlanetCount : 12;
|
||||||
|
document.querySelector('#AstronautCount').value = config.Space.AstronautCount !== undefined ? config.Space.AstronautCount : 5;
|
||||||
|
document.querySelector('#SatelliteCount').value = config.Space.SatelliteCount !== undefined ? config.Space.SatelliteCount : 2;
|
||||||
|
document.querySelector('#IssCount').value = config.Space.IssCount !== undefined ? config.Space.IssCount : 1;
|
||||||
|
document.querySelector('#RocketCount').value = config.Space.RocketCount !== undefined ? config.Space.RocketCount : 1;
|
||||||
|
|
||||||
// Underwater
|
// Underwater
|
||||||
document.querySelector('#EnableUnderwater').checked = config.Underwater.EnableUnderwater || false;
|
document.querySelector('#EnableUnderwater').checked = config.Underwater.EnableUnderwater || false;
|
||||||
@@ -1833,6 +1868,7 @@
|
|||||||
document.querySelector('#EnableRandomSymbolsMobileBirthday').checked = config.Birthday.EnableRandomSymbolsMobile || false;
|
document.querySelector('#EnableRandomSymbolsMobileBirthday').checked = config.Birthday.EnableRandomSymbolsMobile || false;
|
||||||
document.querySelector('#EnableDifferentDurationBirthday').checked = config.Birthday.EnableDifferentDuration || false;
|
document.querySelector('#EnableDifferentDurationBirthday').checked = config.Birthday.EnableDifferentDuration || false;
|
||||||
document.querySelector('#BirthdaySymbolCount').value = config.Birthday.SymbolCount || 25;
|
document.querySelector('#BirthdaySymbolCount').value = config.Birthday.SymbolCount || 25;
|
||||||
|
document.querySelector('#BirthdayConfettiCount').value = config.Birthday.ConfettiCount || 60;
|
||||||
|
|
||||||
// Sports
|
// Sports
|
||||||
if (!config.Sports) config.Sports = { EnableSports: true, SymbolCount: 25, EnableRandomSymbols: true, EnableRandomSymbolsMobile: false, EnableDifferentDuration: true };
|
if (!config.Sports) config.Sports = { EnableSports: true, SymbolCount: 25, EnableRandomSymbols: true, EnableRandomSymbolsMobile: false, EnableDifferentDuration: true };
|
||||||
@@ -1842,6 +1878,7 @@
|
|||||||
document.querySelector('#EnableDifferentDurationSports').checked = config.Sports.EnableDifferentDuration !== false;
|
document.querySelector('#EnableDifferentDurationSports').checked = config.Sports.EnableDifferentDuration !== false;
|
||||||
document.querySelector('#SportsSymbolCount').value = config.Sports.SymbolCount || 25;
|
document.querySelector('#SportsSymbolCount').value = config.Sports.SymbolCount || 25;
|
||||||
document.querySelector('#TurfColor').value = config.Sports.TurfColor || '#228b22';
|
document.querySelector('#TurfColor').value = config.Sports.TurfColor || '#228b22';
|
||||||
|
document.querySelector('#EnableTrophy').checked = config.Sports.EnableTrophy !== false;
|
||||||
|
|
||||||
// Load Checkboxes
|
// Load Checkboxes
|
||||||
const savedBallsString = config.Sports.SportsBalls || 'football,basketball,tennis,volleyball';
|
const savedBallsString = config.Sports.SportsBalls || 'football,basketball,tennis,volleyball';
|
||||||
@@ -2012,6 +2049,7 @@
|
|||||||
document.querySelector('#EnableBirthday').checked = config.Birthday.EnableBirthday !== false;
|
document.querySelector('#EnableBirthday').checked = config.Birthday.EnableBirthday !== false;
|
||||||
document.querySelector('#EnableGarland').checked = config.Birthday.EnableGarland !== false;
|
document.querySelector('#EnableGarland').checked = config.Birthday.EnableGarland !== false;
|
||||||
document.querySelector('#BirthdaySymbolCount').value = config.Birthday.SymbolCount || 25;
|
document.querySelector('#BirthdaySymbolCount').value = config.Birthday.SymbolCount || 25;
|
||||||
|
document.querySelector('#BirthdayConfettiCount').value = config.Birthday.ConfettiCount || 60;
|
||||||
document.querySelector('#EnableRandomSymbolsBirthday').checked = config.Birthday.EnableRandomSymbols !== false;
|
document.querySelector('#EnableRandomSymbolsBirthday').checked = config.Birthday.EnableRandomSymbols !== false;
|
||||||
document.querySelector('#EnableRandomSymbolsMobileBirthday').checked = config.Birthday.EnableRandomSymbolsMobile === true;
|
document.querySelector('#EnableRandomSymbolsMobileBirthday').checked = config.Birthday.EnableRandomSymbolsMobile === true;
|
||||||
document.querySelector('#EnableDifferentDurationBirthday').checked = config.Birthday.EnableDifferentDuration !== false;
|
document.querySelector('#EnableDifferentDurationBirthday').checked = config.Birthday.EnableDifferentDuration !== false;
|
||||||
@@ -2069,6 +2107,7 @@
|
|||||||
config.Sports.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSports').checked;
|
config.Sports.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSports').checked;
|
||||||
config.Sports.SymbolCount = parseInt(document.querySelector('#SportsSymbolCount').value);
|
config.Sports.SymbolCount = parseInt(document.querySelector('#SportsSymbolCount').value);
|
||||||
config.Sports.TurfColor = document.querySelector('#TurfColor').value;
|
config.Sports.TurfColor = document.querySelector('#TurfColor').value;
|
||||||
|
config.Sports.EnableTrophy = document.querySelector('#EnableTrophy').checked;
|
||||||
|
|
||||||
// Save Checkboxes
|
// Save Checkboxes
|
||||||
const selectedBalls = Array.from(document.querySelectorAll('.sport-ball-cb'))
|
const selectedBalls = Array.from(document.querySelectorAll('.sport-ball-cb'))
|
||||||
@@ -2091,7 +2130,11 @@
|
|||||||
config.Space.EnableRandomSymbols = document.querySelector('#EnableRandomSymbolsSpace').checked;
|
config.Space.EnableRandomSymbols = document.querySelector('#EnableRandomSymbolsSpace').checked;
|
||||||
config.Space.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileSpace').checked;
|
config.Space.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileSpace').checked;
|
||||||
config.Space.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSpace').checked;
|
config.Space.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationSpace').checked;
|
||||||
config.Space.SymbolCount = parseInt(document.querySelector('#SpaceSymbolCount').value);
|
config.Space.PlanetCount = parseInt(document.querySelector('#PlanetCount').value);
|
||||||
|
config.Space.AstronautCount = parseInt(document.querySelector('#AstronautCount').value);
|
||||||
|
config.Space.SatelliteCount = parseInt(document.querySelector('#SatelliteCount').value);
|
||||||
|
config.Space.IssCount = parseInt(document.querySelector('#IssCount').value);
|
||||||
|
config.Space.RocketCount = parseInt(document.querySelector('#RocketCount').value);
|
||||||
|
|
||||||
// Underwater
|
// Underwater
|
||||||
if (!config.Underwater) config.Underwater = {};
|
if (!config.Underwater) config.Underwater = {};
|
||||||
@@ -2117,6 +2160,7 @@
|
|||||||
config.Birthday.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileBirthday').checked;
|
config.Birthday.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileBirthday').checked;
|
||||||
config.Birthday.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationBirthday').checked;
|
config.Birthday.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationBirthday').checked;
|
||||||
config.Birthday.SymbolCount = parseInt(document.querySelector('#BirthdaySymbolCount').value);
|
config.Birthday.SymbolCount = parseInt(document.querySelector('#BirthdaySymbolCount').value);
|
||||||
|
config.Birthday.ConfettiCount = parseInt(document.querySelector('#BirthdayConfettiCount').value);
|
||||||
|
|
||||||
// Snowflakes
|
// Snowflakes
|
||||||
config.Snowflakes.SnowflakeCount = parseInt(document.querySelector('#SnowflakesCount').value);
|
config.Snowflakes.SnowflakeCount = parseInt(document.querySelector('#SnowflakesCount').value);
|
||||||
@@ -2383,6 +2427,7 @@
|
|||||||
config.Birthday.EnableBirthday = document.querySelector('#EnableBirthday').checked;
|
config.Birthday.EnableBirthday = document.querySelector('#EnableBirthday').checked;
|
||||||
config.Birthday.EnableGarland = document.querySelector('#EnableGarland').checked;
|
config.Birthday.EnableGarland = document.querySelector('#EnableGarland').checked;
|
||||||
config.Birthday.SymbolCount = parseInt(document.querySelector('#BirthdaySymbolCount').value);
|
config.Birthday.SymbolCount = parseInt(document.querySelector('#BirthdaySymbolCount').value);
|
||||||
|
config.Birthday.ConfettiCount = parseInt(document.querySelector('#BirthdayConfettiCount').value);
|
||||||
config.Birthday.EnableRandomSymbols = document.querySelector('#EnableRandomSymbolsBirthday').checked;
|
config.Birthday.EnableRandomSymbols = document.querySelector('#EnableRandomSymbolsBirthday').checked;
|
||||||
config.Birthday.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileBirthday').checked;
|
config.Birthday.EnableRandomSymbolsMobile = document.querySelector('#EnableRandomSymbolsMobileBirthday').checked;
|
||||||
config.Birthday.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationBirthday').checked;
|
config.Birthday.EnableDifferentDuration = document.querySelector('#EnableDifferentDurationBirthday').checked;
|
||||||
|
|||||||
@@ -10,49 +10,22 @@
|
|||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
.birthday-garland {
|
|
||||||
position: absolute;
|
|
||||||
top: -1vh;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
z-index: 55;
|
|
||||||
pointer-events: none;
|
|
||||||
/* optional: a little drop shadow for depth */
|
|
||||||
filter: drop-shadow(0 5px 8px rgba(0,0,0,0.5));
|
|
||||||
}
|
|
||||||
|
|
||||||
.birthday-garland img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.birthday-cake {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2vh;
|
|
||||||
left: 50vw;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
font-size: 8rem;
|
|
||||||
z-index: 50;
|
|
||||||
filter: drop-shadow(0 0 10px rgba(255,255,255,0.4));
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.birthday-cake img {
|
|
||||||
height: 15vh;
|
|
||||||
width: auto;
|
|
||||||
object-fit: contain;
|
|
||||||
max-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.birthday-symbol {
|
.birthday-symbol {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
bottom: -10vh; /* balloons rise from bottom */
|
top: 0;
|
||||||
animation: birthday-rise linear infinite;
|
left: 0;
|
||||||
font-size: 3rem;
|
animation: birthday-rise linear infinite forwards;
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
pointer-events: none; /* Container itself should not block clicks */
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday-sway {
|
||||||
|
will-change: transform;
|
||||||
|
animation-name: birthday-sway;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-direction: alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.birthday-inner {
|
.birthday-inner {
|
||||||
@@ -61,62 +34,73 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MARK: Balloon Size */
|
||||||
.birthday-symbol img {
|
.birthday-symbol img {
|
||||||
width: 6vh;
|
width: 18vh;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-width: 60px;
|
max-width: 100px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.birthday-confetti-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 30;
|
||||||
|
will-change: transform;
|
||||||
|
animation-name: birthday-confetti-fall;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.birthday-confetti {
|
.birthday-confetti {
|
||||||
position: absolute;
|
width: 8px;
|
||||||
top: -5vh;
|
height: 16px;
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
will-change: transform;
|
||||||
|
animation-name: birthday-flutter;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday-confetti.circle {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday-confetti.square {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday-confetti.triangle {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
opacity: 0.9;
|
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
|
||||||
animation: birthday-confetti-fall linear infinite;
|
|
||||||
z-index: 30;
|
|
||||||
/* Mix of circles and squares by using CSS variables or random in JS. For simplicity, we make all slightly rounded rectangles */
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes birthday-rise {
|
@keyframes birthday-rise {
|
||||||
0% {
|
0% { transform: translate3d(var(--x-pos, 0vw), 110vh, 0) rotate(var(--start-rot, 0deg)); opacity: 0; }
|
||||||
transform: translateY(10vh) rotate(var(--start-rot, 0deg));
|
10% { opacity: 1; }
|
||||||
opacity: 0;
|
90% { opacity: 1; }
|
||||||
}
|
100% { transform: translate3d(var(--x-pos, 0vw), -20vh, 0) rotate(calc(var(--start-rot, 0deg) * -1)); opacity: 0; }
|
||||||
10% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateY(-110vh) rotate(calc(var(--start-rot, 0deg) * -1));
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes birthday-confetti-fall {
|
@keyframes birthday-confetti-fall {
|
||||||
0% {
|
0% { transform: translate3d(var(--x-pos, 0vw), -10vh, 0); }
|
||||||
transform: translateY(-5vh) rotateX(0deg) rotateY(0deg) rotateZ(0deg);
|
100% { transform: translate3d(var(--x-pos, 0vw), 110vh, 0); }
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
5% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translateY(105vh) rotateX(720deg) rotateY(360deg) rotateZ(180deg);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes birthday-sway {
|
@keyframes birthday-sway {
|
||||||
0% { transform: rotate(-8deg) translateX(-5%); }
|
0% { transform: translateX(calc(var(--sway-amount, 50px) * -1)); }
|
||||||
100% { transform: rotate(8deg) translateX(5%); }
|
100% { transform: translateX(var(--sway-amount, 50px)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes birthday-flutter {
|
||||||
|
0% { transform: rotate3d(var(--rx, 1), var(--ry, 1), var(--rz, 0), 0deg); }
|
||||||
|
100% { transform: rotate3d(var(--rx, 1), var(--ry, 1), var(--rz, 0), var(--rot-dir, 360deg)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes birthday-pop {
|
@keyframes birthday-pop {
|
||||||
@@ -125,12 +109,17 @@
|
|||||||
100% { transform: scale(0); opacity: 0; filter: brightness(2); }
|
100% { transform: scale(0); opacity: 0; filter: brightness(2); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.birthday-burst-confetti {
|
.birthday-burst-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
will-change: transform, opacity;
|
will-change: transform, opacity;
|
||||||
animation: birthday-burst-fall 1.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
animation: birthday-burst-y 1.2s cubic-bezier(0.42, 0, 1, 1) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.birthday-burst-confetti {
|
||||||
|
will-change: transform;
|
||||||
|
animation: birthday-burst-x 1.2s cubic-bezier(0.25, 1, 0.5, 1) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.birthday-burst-confetti.circle {
|
.birthday-burst-confetti.circle {
|
||||||
@@ -138,25 +127,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.birthday-burst-confetti.triangle {
|
.birthday-burst-confetti.triangle {
|
||||||
width: 0;
|
width: 10px;
|
||||||
height: 0;
|
height: 10px;
|
||||||
background-color: transparent !important;
|
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
|
||||||
border-left: 5px solid transparent;
|
|
||||||
border-right: 5px solid transparent;
|
|
||||||
border-bottom: 10px solid var(--shape-color, #ff0000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes birthday-burst-fall {
|
@keyframes birthday-burst-y {
|
||||||
0% {
|
0% {
|
||||||
transform: translate(0, 0) rotate3d(var(--rx), var(--ry), var(--rz), 0deg);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
30% {
|
|
||||||
transform: translate(var(--burst-x), var(--burst-y)) rotate3d(var(--rx), var(--ry), var(--rz), calc(var(--rot-dir) * 0.3));
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translate(var(--burst-x), calc(var(--burst-y) + 150px)) rotate3d(var(--rx), var(--ry), var(--rz), var(--rot-dir));
|
transform: translateY(calc(var(--burst-y) + 150px)); /* Gravity pull downwards */
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes birthday-burst-x {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0) rotate3d(var(--rx), var(--ry), var(--rz), 0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(calc(var(--burst-x) * 1.5)) rotate3d(var(--rx), var(--ry), var(--rz), var(--rot-dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,37 @@
|
|||||||
const config = window.SeasonalsPluginConfig?.Birthday || {};
|
const config = window.SeasonalsPluginConfig?.Birthday || {};
|
||||||
|
|
||||||
const birthday = config.EnableBirthday !== undefined ? config.EnableBirthday : true;
|
const birthday = config.EnableBirthday !== undefined ? config.EnableBirthday : true;
|
||||||
const symbolCount = config.SymbolCount || 25;
|
const symbolCount = config.SymbolCount || 5;
|
||||||
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
||||||
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
||||||
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
||||||
const enableGarland = config.EnableGarland !== undefined ? config.EnableGarland : true;
|
|
||||||
|
|
||||||
const birthdayImages = [
|
const birthdayImages = [
|
||||||
'../Seasonals/Resources/birthday_images/balloon_1.gif',
|
'../Seasonals/Resources/birthday_assets/balloon_blue.gif',
|
||||||
'../Seasonals/Resources/birthday_images/balloon_2.gif',
|
'../Seasonals/Resources/birthday_assets/balloon_green.gif',
|
||||||
'../Seasonals/Resources/birthday_images/balloon_3.gif'
|
'../Seasonals/Resources/birthday_assets/balloon_lightblue.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_orange.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_pink.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_red.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_yellow.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_turquoise.gif',
|
||||||
|
'../Seasonals/Resources/birthday_assets/balloon_violet.gif'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
const balloonColors = {
|
||||||
|
'balloon_blue': ['#3498db', '#2980b9', '#1f618d'],
|
||||||
|
'balloon_green': ['#2ecc71', '#27ae60', '#1e8449'],
|
||||||
|
'balloon_lightblue': ['#36c5f0', '#81ecec', '#00cec9'],
|
||||||
|
'balloon_orange': ['#e67e22', '#d35400', '#a04000'],
|
||||||
|
'balloon_pink': ['#ff726d', '#f4306d', '#e84393'],
|
||||||
|
'balloon_red': ['#e74c3c', '#c0392b', '#922b21'],
|
||||||
|
'balloon_yellow': ['#f1c40f', '#f39c12', '#b7950b'],
|
||||||
|
'balloon_turquoise': ['#36c5f0', '#81ecec', '#00cec9'],
|
||||||
|
'balloon_violet': ['#9b59b6', '#8e44ad', '#6c3483']
|
||||||
|
};
|
||||||
|
|
||||||
let msgPrinted = false;
|
let msgPrinted = false;
|
||||||
|
|
||||||
function toggleBirthday() {
|
function toggleBirthday() {
|
||||||
@@ -46,8 +65,8 @@ observer.observe(document.body, {
|
|||||||
attributes: true
|
attributes: true
|
||||||
});
|
});
|
||||||
|
|
||||||
function createBalloonPopConfetti(container, x, y) {
|
function createBalloonPopConfetti(container, x, y, colors) {
|
||||||
const popConfettiColors = [
|
const popConfettiColors = colors || [
|
||||||
'#fce18a', '#ff726d', '#b48def', '#f4306d',
|
'#fce18a', '#ff726d', '#b48def', '#f4306d',
|
||||||
'#36c5f0', '#2ccc5d', '#e9b31d', '#9b59b6',
|
'#36c5f0', '#2ccc5d', '#e9b31d', '#9b59b6',
|
||||||
'#3498db', '#e74c3c', '#1abc9c', '#f1c40f'
|
'#3498db', '#e74c3c', '#1abc9c', '#f1c40f'
|
||||||
@@ -57,6 +76,13 @@ function createBalloonPopConfetti(container, x, y) {
|
|||||||
const particleCount = Math.floor(Math.random() * 5) + 15;
|
const particleCount = Math.floor(Math.random() * 5) + 15;
|
||||||
|
|
||||||
for (let i = 0; i < particleCount; i++) {
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'birthday-burst-wrapper';
|
||||||
|
wrapper.style.position = 'absolute';
|
||||||
|
wrapper.style.left = `${x}px`;
|
||||||
|
wrapper.style.top = `${y}px`;
|
||||||
|
wrapper.style.zIndex = '1000';
|
||||||
|
|
||||||
const particle = document.createElement('div');
|
const particle = document.createElement('div');
|
||||||
particle.classList.add('birthday-burst-confetti');
|
particle.classList.add('birthday-burst-confetti');
|
||||||
|
|
||||||
@@ -81,11 +107,6 @@ function createBalloonPopConfetti(container, x, y) {
|
|||||||
particle.classList.add('triangle');
|
particle.classList.add('triangle');
|
||||||
}
|
}
|
||||||
|
|
||||||
particle.style.position = 'absolute';
|
|
||||||
particle.style.left = `${x}px`;
|
|
||||||
particle.style.top = `${y}px`;
|
|
||||||
particle.style.zIndex = '1000';
|
|
||||||
|
|
||||||
// Random direction for explosion (circular)
|
// Random direction for explosion (circular)
|
||||||
const angle = Math.random() * 2 * Math.PI;
|
const angle = Math.random() * 2 * Math.PI;
|
||||||
const distance = Math.random() * 60 + 20; // 20-80px burst radius
|
const distance = Math.random() * 60 + 20; // 20-80px burst radius
|
||||||
@@ -94,7 +115,7 @@ function createBalloonPopConfetti(container, x, y) {
|
|||||||
const yOffset = Math.sin(angle) * distance;
|
const yOffset = Math.sin(angle) * distance;
|
||||||
|
|
||||||
particle.style.setProperty('--burst-x', `${xOffset}px`);
|
particle.style.setProperty('--burst-x', `${xOffset}px`);
|
||||||
particle.style.setProperty('--burst-y', `${yOffset}px`);
|
wrapper.style.setProperty('--burst-y', `${yOffset}px`);
|
||||||
|
|
||||||
// Random rotation during fall
|
// Random rotation during fall
|
||||||
particle.style.setProperty('--rot-dir', `${(Math.random() > 0.5 ? 1 : -1) * 360}deg`);
|
particle.style.setProperty('--rot-dir', `${(Math.random() > 0.5 ? 1 : -1) * 360}deg`);
|
||||||
@@ -102,10 +123,11 @@ function createBalloonPopConfetti(container, x, y) {
|
|||||||
particle.style.setProperty('--ry', Math.random().toFixed(2));
|
particle.style.setProperty('--ry', Math.random().toFixed(2));
|
||||||
particle.style.setProperty('--rz', (Math.random() * 0.5).toFixed(2));
|
particle.style.setProperty('--rz', (Math.random() * 0.5).toFixed(2));
|
||||||
|
|
||||||
container.appendChild(particle);
|
wrapper.appendChild(particle);
|
||||||
|
container.appendChild(wrapper);
|
||||||
|
|
||||||
// Remove particle after animation
|
// Remove particle after animation
|
||||||
setTimeout(() => particle.remove(), 1500);
|
setTimeout(() => wrapper.remove(), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,28 +140,7 @@ function createBirthday() {
|
|||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Party Garland
|
// Cake and Garland have been removed
|
||||||
if (enableGarland) {
|
|
||||||
const garland = document.createElement('div');
|
|
||||||
garland.className = 'birthday-garland';
|
|
||||||
const garlandImg = document.createElement('img');
|
|
||||||
garlandImg.src = '../Seasonals/Resources/birthday_images/garland.png';
|
|
||||||
garlandImg.onerror = function() { this.style.display = 'none'; };
|
|
||||||
garland.appendChild(garlandImg);
|
|
||||||
container.appendChild(garland);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawn Birthday Cake at the bottom
|
|
||||||
const cake = document.createElement('div');
|
|
||||||
cake.className = 'birthday-cake';
|
|
||||||
let cakeImg = document.createElement('img');
|
|
||||||
cakeImg.src = `../Seasonals/Resources/birthday_images/cake.gif`;
|
|
||||||
cakeImg.onerror = function() {
|
|
||||||
this.style.display = 'none';
|
|
||||||
this.parentElement.innerHTML = '🎂';
|
|
||||||
};
|
|
||||||
cake.appendChild(cakeImg);
|
|
||||||
container.appendChild(cake);
|
|
||||||
|
|
||||||
const standardCount = 15;
|
const standardCount = 15;
|
||||||
const totalSymbols = symbolCount + standardCount;
|
const totalSymbols = symbolCount + standardCount;
|
||||||
@@ -153,13 +154,13 @@ function createBirthday() {
|
|||||||
|
|
||||||
const useRandomDuration = enableDifferentDuration !== false;
|
const useRandomDuration = enableDifferentDuration !== false;
|
||||||
|
|
||||||
// We'll treat all balloons as rising symbols
|
// Arrays moved to top of file
|
||||||
const activeItems = ['balloon_1', 'balloon_2', 'balloon_3'];
|
|
||||||
|
|
||||||
for (let i = 0; i < finalCount; i++) {
|
for (let i = 0; i < finalCount; i++) {
|
||||||
let symbol = document.createElement('div');
|
let symbol = document.createElement('div');
|
||||||
|
|
||||||
const randomItem = activeItems[Math.floor(Math.random() * activeItems.length)];
|
const randomImage = birthdayImages[Math.floor(Math.random() * birthdayImages.length)];
|
||||||
|
const randomItem = randomImage.split('/').pop().split('.')[0]; // Extracts "balloon_blue"
|
||||||
symbol.className = `birthday-symbol birthday-${randomItem}`;
|
symbol.className = `birthday-symbol birthday-${randomItem}`;
|
||||||
|
|
||||||
// Create inner div for sway
|
// Create inner div for sway
|
||||||
@@ -167,20 +168,31 @@ function createBirthday() {
|
|||||||
innerDiv.className = 'birthday-inner';
|
innerDiv.className = 'birthday-inner';
|
||||||
|
|
||||||
let img = document.createElement('img');
|
let img = document.createElement('img');
|
||||||
img.src = `../Seasonals/Resources/birthday_images/${randomItem}.gif`; // Use standard pop GIFs
|
img.src = randomImage;
|
||||||
img.onerror = function() {
|
img.onerror = function() {
|
||||||
this.style.display = 'none';
|
symbol.remove(); // Remove element completely on error
|
||||||
this.parentElement.innerHTML = getBirthdayEmojiFallback(randomItem);
|
|
||||||
};
|
};
|
||||||
innerDiv.appendChild(img);
|
innerDiv.appendChild(img);
|
||||||
symbol.appendChild(innerDiv);
|
|
||||||
|
// Sway wrapper
|
||||||
|
let swayWrapper = document.createElement('div');
|
||||||
|
swayWrapper.className = 'birthday-sway';
|
||||||
|
const swayDuration = Math.random() * 3 + 3; // 3-6s per cycle
|
||||||
|
swayWrapper.style.animationDuration = `${swayDuration}s`;
|
||||||
|
swayWrapper.style.animationDelay = `-${Math.random() * 5}s`;
|
||||||
|
const swayAmount = Math.random() * 60 + 20; // 20-80px
|
||||||
|
const direction = Math.random() > 0.5 ? 1 : -1;
|
||||||
|
swayWrapper.style.setProperty('--sway-amount', `${swayAmount * direction}px`);
|
||||||
|
|
||||||
|
swayWrapper.appendChild(innerDiv);
|
||||||
|
symbol.appendChild(swayWrapper);
|
||||||
|
|
||||||
const leftPos = Math.random() * 95;
|
const leftPos = Math.random() * 95;
|
||||||
const delaySeconds = Math.random() * 10;
|
|
||||||
|
|
||||||
// Far away effect
|
// Far away effect
|
||||||
const depth = Math.random();
|
const depth = Math.random();
|
||||||
const scale = 0.5 + depth * 0.7; // 0.5 to 1.2
|
// MARK: balloon size
|
||||||
|
const scale = 0.85 + depth * 0.3; // 0.85 to 1.15
|
||||||
const zIndex = Math.floor(depth * 30) + 10;
|
const zIndex = Math.floor(depth * 30) + 10;
|
||||||
|
|
||||||
img.style.transform = `scale(${scale})`;
|
img.style.transform = `scale(${scale})`;
|
||||||
@@ -192,13 +204,14 @@ function createBirthday() {
|
|||||||
durationSeconds = (1 - depth) * 6 + 7 + Math.random() * 4;
|
durationSeconds = (1 - depth) * 6 + 7 + Math.random() * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Negative delay correctly scatters them initially across the screen vertically
|
||||||
|
// avoiding them all popping up at bottom edge together
|
||||||
|
const delaySeconds = -(Math.random() * durationSeconds);
|
||||||
|
|
||||||
const isBalloon = randomItem.startsWith('balloon');
|
const isBalloon = randomItem.startsWith('balloon');
|
||||||
|
|
||||||
if (isBalloon) {
|
if (isBalloon) {
|
||||||
// Sway animation
|
// Sway animation is now handled natively by the GIF motion.
|
||||||
const swayDur = Math.random() * 2 + 3; // 3 to 5s
|
|
||||||
const swayDir = Math.random() > 0.5 ? 'normal' : 'reverse';
|
|
||||||
innerDiv.style.animation = `birthday-sway ${swayDur}s ease-in-out infinite alternate ${swayDir}`;
|
|
||||||
|
|
||||||
// Interaction to pop is handled visually by the GIF, but we can still remove it on hover
|
// Interaction to pop is handled visually by the GIF, but we can still remove it on hover
|
||||||
innerDiv.addEventListener('mouseenter', function(e) {
|
innerDiv.addEventListener('mouseenter', function(e) {
|
||||||
@@ -210,17 +223,18 @@ function createBirthday() {
|
|||||||
// Create confetti burst at balloon's screen position
|
// Create confetti burst at balloon's screen position
|
||||||
const rect = this.getBoundingClientRect();
|
const rect = this.getBoundingClientRect();
|
||||||
const cx = rect.left + rect.width / 2;
|
const cx = rect.left + rect.width / 2;
|
||||||
const cy = rect.top + rect.height / 2;
|
// explosion height
|
||||||
|
const cy = rect.top + rect.height * -0.05;
|
||||||
// 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);
|
createBalloonPopConfetti(document.body, cx, cy, balloonColors[randomItem]);
|
||||||
}
|
}
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const startRot = (Math.random() * 20) - 10; // -10 to +10 spread
|
const startRot = (Math.random() * 20) - 10; // -10 to +10 spread
|
||||||
symbol.style.setProperty('--start-rot', `${startRot}deg`);
|
symbol.style.setProperty('--start-rot', `${startRot}deg`);
|
||||||
|
symbol.style.setProperty('--x-pos', `${leftPos}vw`);
|
||||||
|
|
||||||
symbol.style.left = `${leftPos}vw`;
|
|
||||||
symbol.style.animationDuration = `${durationSeconds}s`;
|
symbol.style.animationDuration = `${durationSeconds}s`;
|
||||||
symbol.style.animationDelay = `${delaySeconds}s`;
|
symbol.style.animationDelay = `${delaySeconds}s`;
|
||||||
|
|
||||||
@@ -228,33 +242,73 @@ function createBirthday() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Party Confetti
|
// Party Confetti
|
||||||
const confettiColors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000'];
|
const baseConfettiCount = config.ConfettiCount !== undefined ? config.ConfettiCount : 60;
|
||||||
const confettiCount = isMobile ? 40 : 80;
|
const confettiCount = isMobile ? Math.floor(baseConfettiCount / 2) : baseConfettiCount;
|
||||||
|
const allColors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000'];
|
||||||
|
|
||||||
for (let i = 0; i < confettiCount; i++) {
|
for (let i = 0; i < confettiCount; i++) {
|
||||||
let confetti = document.createElement('div');
|
const wrapper = document.createElement('div');
|
||||||
confetti.className = 'birthday-confetti';
|
wrapper.classList.add('birthday-confetti-wrapper');
|
||||||
|
|
||||||
const color = confettiColors[Math.floor(Math.random() * confettiColors.length)];
|
// Use carnival.js 3D advanced fluttering logic
|
||||||
|
let swayWrapper = document.createElement('div');
|
||||||
|
swayWrapper.classList.add('birthday-sway');
|
||||||
|
wrapper.appendChild(swayWrapper);
|
||||||
|
|
||||||
|
const confetti = document.createElement('div');
|
||||||
|
confetti.classList.add('birthday-confetti');
|
||||||
|
|
||||||
|
const color = allColors[Math.floor(Math.random() * allColors.length)];
|
||||||
confetti.style.backgroundColor = color;
|
confetti.style.backgroundColor = color;
|
||||||
|
|
||||||
const leftPos = Math.random() * 100;
|
// Shape assignments
|
||||||
const delaySeconds = Math.random() * 8;
|
const shape = Math.random();
|
||||||
const duration = Math.random() * 3 + 4;
|
if (shape > 0.8) confetti.classList.add('circle');
|
||||||
|
else if (shape > 0.6) confetti.classList.add('square');
|
||||||
|
else if (shape > 0.4) confetti.classList.add('triangle');
|
||||||
|
else confetti.classList.add('rect'); // default
|
||||||
|
|
||||||
confetti.style.left = `${leftPos}vw`;
|
// Sizing
|
||||||
confetti.style.animationDuration = `${duration}s`;
|
if (!confetti.classList.contains('circle') && !confetti.classList.contains('square') && !confetti.classList.contains('triangle')) {
|
||||||
confetti.style.animationDelay = `${delaySeconds}s`;
|
const width = Math.random() * 3 + 4; // 4-7px
|
||||||
|
const height = Math.random() * 5 + 8; // 8-13px
|
||||||
|
confetti.style.width = `${width}px`;
|
||||||
|
confetti.style.height = `${height}px`;
|
||||||
|
} else if (confetti.classList.contains('circle') || confetti.classList.contains('square')) {
|
||||||
|
const size = Math.random() * 5 + 5; // 5-10px
|
||||||
|
confetti.style.width = `${size}px`;
|
||||||
|
confetti.style.height = `${size}px`;
|
||||||
|
}
|
||||||
|
|
||||||
container.appendChild(confetti);
|
const duration = Math.random() * 5 + 5;
|
||||||
|
const delay = -Math.random() * duration; // Spawn fully integrated across screen width/height
|
||||||
|
|
||||||
|
wrapper.style.setProperty('--x-pos', `${Math.random() * 100}vw`);
|
||||||
|
wrapper.style.animationDelay = `${delay}s`;
|
||||||
|
wrapper.style.animationDuration = `${duration}s`;
|
||||||
|
|
||||||
|
// Sway handling
|
||||||
|
const swayDuration = Math.random() * 2 + 3; // 3-5s per cycle
|
||||||
|
swayWrapper.style.animationDuration = `${swayDuration}s`;
|
||||||
|
swayWrapper.style.animationDelay = `-${Math.random() * 5}s`;
|
||||||
|
const swayAmount = Math.random() * 70 + 30; // 30-100px
|
||||||
|
const direction = Math.random() > 0.5 ? 1 : -1;
|
||||||
|
swayWrapper.style.setProperty('--sway-amount', `${swayAmount * direction}px`);
|
||||||
|
|
||||||
|
// 3D Flutter Rotation
|
||||||
|
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));
|
||||||
|
const rotDir = Math.random() > 0.5 ? 1 : -1;
|
||||||
|
confetti.style.setProperty('--rot-dir', `${rotDir * 360}deg`);
|
||||||
|
|
||||||
|
swayWrapper.appendChild(confetti);
|
||||||
|
container.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBirthdayEmojiFallback(type) {
|
/* Removed fallback logic */
|
||||||
if (type.startsWith('balloon')) return '🎈';
|
|
||||||
if (type === 'gift') return '🎁';
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeBirthday() {
|
function initializeBirthday() {
|
||||||
if (!birthday) return;
|
if (!birthday) return;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
.olympia-symbol {
|
.olympia-symbol {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10vh;
|
top: -10vh;
|
||||||
font-size: 3rem;
|
|
||||||
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;
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
.olympia-symbol {
|
.olympia-symbol {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10vh;
|
top: -10vh;
|
||||||
font-size: 4rem;
|
|
||||||
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;
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ function createOlympia() {
|
|||||||
}
|
}
|
||||||
img.src = `../Seasonals/Resources/olympic_assets/${imgName}`;
|
img.src = `../Seasonals/Resources/olympic_assets/${imgName}`;
|
||||||
img.onerror = function() {
|
img.onerror = function() {
|
||||||
this.style.display = 'none';
|
symbol.remove();
|
||||||
this.parentElement.innerHTML = getOlympiaEmojiFallback(randomItem);
|
|
||||||
};
|
};
|
||||||
innerDiv.appendChild(img);
|
innerDiv.appendChild(img);
|
||||||
|
|
||||||
@@ -244,13 +243,6 @@ function createOlympia() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOlympiaEmojiFallback(type) {
|
|
||||||
if (type === 'gold') return '🥇';
|
|
||||||
if (type === 'silver') return '🥈';
|
|
||||||
if (type === 'bronze') return '🥉';
|
|
||||||
return ''; // Rings will be handled by CSS or actual image
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeOlympia() {
|
function initializeOlympia() {
|
||||||
if (!olympia) return;
|
if (!olympia) return;
|
||||||
createOlympia();
|
createOlympia();
|
||||||
|
|||||||
@@ -36,18 +36,19 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background: linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
|
background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: space-shoot 8s linear infinite;
|
animation: space-shoot 25s linear infinite;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
z-index: 12;
|
z-index: 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes space-shoot {
|
@keyframes space-shoot {
|
||||||
0% { left: var(--shoot-start-x); top: var(--shoot-start-y); opacity: 1; width: 0; }
|
0% { transform: rotate(var(--shoot-angle)) translateX(0); opacity: 0; }
|
||||||
5% { width: 300px; }
|
5% { opacity: 1; }
|
||||||
15% { left: var(--shoot-end-x); top: var(--shoot-end-y); opacity: 0; width: 0; }
|
35% { opacity: 1; }
|
||||||
100% { left: var(--shoot-end-x); top: var(--shoot-end-y); opacity: 0; width: 0; }
|
40% { transform: rotate(var(--shoot-angle)) translateX(var(--shoot-distance)); opacity: 0; }
|
||||||
|
100% { transform: rotate(var(--shoot-angle)) translateX(var(--shoot-distance)); opacity: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.space-symbol {
|
.space-symbol {
|
||||||
@@ -69,24 +70,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Specific elements scaling */
|
/* Specific elements scaling */
|
||||||
.space-planet1, .space-planet2 { font-size: 4rem; }
|
.space-planet img { width: 8vh; max-width: 80px; }
|
||||||
.space-planet1 img, .space-planet2 img { width: 8vh; max-width: 80px; }
|
.space-astronaut img { width: 10vh; max-width: 100px; }
|
||||||
.space-star { font-size: 2rem; opacity: 0.6; }
|
.space-satellite img { width: 12vh; max-width: 120px; }
|
||||||
.space-star img { width: 3vh; max-width: 30px; }
|
.space-iss img { width: 25vh; max-width: 180px; }
|
||||||
|
.space-rocket img { width: 12vh; max-width: 120px; }
|
||||||
|
|
||||||
@keyframes space-drift-right {
|
@keyframes space-drift-right {
|
||||||
0% { transform: translateX(-10vw) translateY(0) scaleX(-1); }
|
0% { transform: translateX(-10vw) translateY(0) scaleX(-1); }
|
||||||
50% { transform: translateX(50vw) translateY(-5vh) scaleX(-1); }
|
50% { transform: translateX(60vw) translateY(-30vh) scaleX(-1); }
|
||||||
100% { transform: translateX(110vw) translateY(0) scaleX(-1); }
|
100% { transform: translateX(140vw) translateY(0) scaleX(-1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes space-drift-left {
|
@keyframes space-drift-left {
|
||||||
0% { transform: translateX(10vw) translateY(0); }
|
0% { transform: translateX(10vw) translateY(0); }
|
||||||
50% { transform: translateX(-50vw) translateY(5vh); }
|
50% { transform: translateX(-60vw) translateY(30vh); }
|
||||||
100% { transform: translateX(-110vw) translateY(0); }
|
100% { transform: translateX(-140vw) translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes space-slow-spin {
|
@keyframes space-slow-spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% { transform: rotate(0deg); }
|
||||||
100% { transform: rotate(360deg); }
|
100% { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes space-star-drift {
|
||||||
|
from { transform: translateY(0); }
|
||||||
|
to { transform: translateY(-100vh); }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
const config = window.SeasonalsPluginConfig?.Space || {};
|
const config = window.SeasonalsPluginConfig?.Space || {};
|
||||||
|
|
||||||
const space = config.EnableSpace !== undefined ? config.EnableSpace : true;
|
const space = config.EnableSpace !== undefined ? config.EnableSpace : true;
|
||||||
const symbolCount = config.SymbolCount || 25;
|
const planetCountConf = config.PlanetCount !== undefined ? config.PlanetCount : 6;
|
||||||
|
const astronautCountConf = config.AstronautCount !== undefined ? config.AstronautCount : 1;
|
||||||
|
const satelliteCountConf = config.SatelliteCount !== undefined ? config.SatelliteCount : 4;
|
||||||
|
const issCountConf = config.IssCount !== undefined ? config.IssCount : 1;
|
||||||
|
const rocketCountConf = config.RocketCount !== undefined ? config.RocketCount : 1;
|
||||||
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
||||||
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
||||||
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
||||||
|
|
||||||
const spaceImages = [
|
const astronautImages = [
|
||||||
"../Seasonals/Resources/space_assets/astronaut_1.gif",
|
"../Seasonals/Resources/space_assets/astronaut_1.gif"
|
||||||
|
];
|
||||||
|
const planetImages = [
|
||||||
"../Seasonals/Resources/space_assets/planet_1.png",
|
"../Seasonals/Resources/space_assets/planet_1.png",
|
||||||
"../Seasonals/Resources/space_assets/planet_2.png",
|
"../Seasonals/Resources/space_assets/planet_2.png",
|
||||||
"../Seasonals/Resources/space_assets/planet_3.png",
|
"../Seasonals/Resources/space_assets/planet_3.png",
|
||||||
@@ -16,13 +22,19 @@ const spaceImages = [
|
|||||||
"../Seasonals/Resources/space_assets/planet_6.png",
|
"../Seasonals/Resources/space_assets/planet_6.png",
|
||||||
"../Seasonals/Resources/space_assets/planet_7.png",
|
"../Seasonals/Resources/space_assets/planet_7.png",
|
||||||
"../Seasonals/Resources/space_assets/planet_8.png",
|
"../Seasonals/Resources/space_assets/planet_8.png",
|
||||||
"../Seasonals/Resources/space_assets/planet_9.png",
|
"../Seasonals/Resources/space_assets/planet_9.png"
|
||||||
"../Seasonals/Resources/space_assets/rocket.gif",
|
|
||||||
"../Seasonals/Resources/space_assets/Satellite_1.gif",
|
|
||||||
"../Seasonals/Resources/space_assets/Satellite_2.gif",
|
|
||||||
"../Seasonals/Resources/space_assets/space-shuttle.png",
|
|
||||||
"../Seasonals/Resources/space_assets/iss.png"
|
|
||||||
];
|
];
|
||||||
|
const satelliteImages = [
|
||||||
|
"../Seasonals/Resources/space_assets/Satellite_1.gif",
|
||||||
|
"../Seasonals/Resources/space_assets/Satellite_2.gif"
|
||||||
|
];
|
||||||
|
|
||||||
|
const issImage = "../Seasonals/Resources/space_assets/iss.png";
|
||||||
|
|
||||||
|
const rocketImages = [
|
||||||
|
"../Seasonals/Resources/space_assets/rocket.gif",
|
||||||
|
"../Seasonals/Resources/space_assets/space-shuttle.png"
|
||||||
|
]
|
||||||
|
|
||||||
let msgPrinted = false;
|
let msgPrinted = false;
|
||||||
|
|
||||||
@@ -66,14 +78,25 @@ function createSpace() {
|
|||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
const standardCount = 15;
|
const standardPlanetCount = 4;
|
||||||
const totalSymbols = symbolCount + standardCount;
|
const standardAstronautCount = 1;
|
||||||
|
const standardSatelliteCount = 2;
|
||||||
|
const standardIssCount = 1;
|
||||||
|
const standardRocketCount = 1;
|
||||||
|
|
||||||
let isMobile = window.matchMedia("only screen and (max-width: 768px)").matches;
|
let isMobile = window.matchMedia("only screen and (max-width: 768px)").matches;
|
||||||
let finalCount = totalSymbols;
|
let pCount = planetCountConf;
|
||||||
|
let aCount = astronautCountConf;
|
||||||
|
let sCount = satelliteCountConf;
|
||||||
|
let iCount = issCountConf;
|
||||||
|
let rCount = rocketCountConf;
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile && !enableRandomMobile) {
|
||||||
finalCount = enableRandomMobile ? totalSymbols : standardCount;
|
pCount = standardPlanetCount;
|
||||||
|
aCount = standardAstronautCount;
|
||||||
|
sCount = standardSatelliteCount;
|
||||||
|
iCount = standardIssCount;
|
||||||
|
rCount = standardRocketCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Nebula Glow
|
// Add Nebula Glow
|
||||||
@@ -90,30 +113,43 @@ function createSpace() {
|
|||||||
|
|
||||||
// Generate random stars for parallax starfield using CSS % / vw sizes for responsiveness
|
// Generate random stars for parallax starfield using CSS % / vw sizes for responsiveness
|
||||||
for (let i = 0; i < 150; i++) {
|
for (let i = 0; i < 150; i++) {
|
||||||
boxShadows1.push(`${(Math.random() * 100).toFixed(2)}vw ${(Math.random() * 100).toFixed(2)}vh #FFF`);
|
let x = (Math.random() * 100).toFixed(2);
|
||||||
if (i < 50) boxShadows2.push(`${(Math.random() * 100).toFixed(2)}vw ${(Math.random() * 100).toFixed(2)}vh #FFF`);
|
let y = (Math.random() * 100).toFixed(2);
|
||||||
if (i < 20) boxShadows3.push(`${(Math.random() * 100).toFixed(2)}vw ${(Math.random() * 100).toFixed(2)}vh #FFF`);
|
boxShadows1.push(`${x}vw ${y}vh #FFF`);
|
||||||
|
boxShadows1.push(`${x}vw ${(parseFloat(y) + 100).toFixed(2)}vh #FFF`);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
let x = (Math.random() * 100).toFixed(2);
|
||||||
|
let y = (Math.random() * 100).toFixed(2);
|
||||||
|
boxShadows2.push(`${x}vw ${y}vh #FFF`);
|
||||||
|
boxShadows2.push(`${x}vw ${(parseFloat(y) + 100).toFixed(2)}vh #FFF`);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
let x = (Math.random() * 100).toFixed(2);
|
||||||
|
let y = (Math.random() * 100).toFixed(2);
|
||||||
|
boxShadows3.push(`${x}vw ${y}vh #FFF`);
|
||||||
|
boxShadows3.push(`${x}vw ${(parseFloat(y) + 100).toFixed(2)}vh #FFF`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const starLayer1 = document.createElement('div');
|
const starLayer1 = document.createElement('div');
|
||||||
starLayer1.style.width = '1px'; starLayer1.style.height = '1px';
|
starLayer1.style.width = '1px'; starLayer1.style.height = '1px';
|
||||||
starLayer1.style.background = 'transparent';
|
starLayer1.style.background = 'transparent';
|
||||||
starLayer1.style.boxShadow = boxShadows1.join(", ");
|
starLayer1.style.boxShadow = boxShadows1.join(", ");
|
||||||
starLayer1.style.animation = 'space-slow-spin 200s linear infinite';
|
starLayer1.style.animation = 'space-star-drift 200s linear infinite';
|
||||||
starfield.appendChild(starLayer1);
|
starfield.appendChild(starLayer1);
|
||||||
|
|
||||||
const starLayer2 = document.createElement('div');
|
const starLayer2 = document.createElement('div');
|
||||||
starLayer2.style.width = '2px'; starLayer2.style.height = '2px';
|
starLayer2.style.width = '2px'; starLayer2.style.height = '2px';
|
||||||
starLayer2.style.background = 'transparent';
|
starLayer2.style.background = 'transparent';
|
||||||
starLayer2.style.boxShadow = boxShadows2.join(", ");
|
starLayer2.style.boxShadow = boxShadows2.join(", ");
|
||||||
starLayer2.style.animation = 'space-slow-spin 150s linear infinite reverse';
|
starLayer2.style.animation = 'space-star-drift 150s linear infinite';
|
||||||
starfield.appendChild(starLayer2);
|
starfield.appendChild(starLayer2);
|
||||||
|
|
||||||
const starLayer3 = document.createElement('div');
|
const starLayer3 = document.createElement('div');
|
||||||
starLayer3.style.width = '3px'; starLayer3.style.height = '3px';
|
starLayer3.style.width = '3px'; starLayer3.style.height = '3px';
|
||||||
starLayer3.style.background = 'transparent';
|
starLayer3.style.background = 'transparent';
|
||||||
starLayer3.style.boxShadow = boxShadows3.join(", ");
|
starLayer3.style.boxShadow = boxShadows3.join(", ");
|
||||||
starLayer3.style.animation = 'space-slow-spin 100s linear infinite';
|
starLayer3.style.animation = 'space-star-drift 100s linear infinite';
|
||||||
starfield.appendChild(starLayer3);
|
starfield.appendChild(starLayer3);
|
||||||
|
|
||||||
container.appendChild(starfield);
|
container.appendChild(starfield);
|
||||||
@@ -125,46 +161,53 @@ function createSpace() {
|
|||||||
streak.className = 'space-shooting-star';
|
streak.className = 'space-shooting-star';
|
||||||
// Pick a random tail direction and fall direction to match
|
// Pick a random tail direction and fall direction to match
|
||||||
const isFromLeft = Math.random() > 0.5;
|
const isFromLeft = Math.random() > 0.5;
|
||||||
const rotateAngle = isFromLeft ? 45 : -45;
|
// Direction angle: random between 15deg-75deg (left) or 105deg-165deg (right)
|
||||||
|
// so they don't always fall in the exact same quadrant trajectory
|
||||||
|
let angle = isFromLeft
|
||||||
|
? Math.random() * 60 + 15
|
||||||
|
: Math.random() * 60 + 105;
|
||||||
|
|
||||||
streak.style.transform = `rotate(${rotateAngle}deg)`;
|
streak.style.setProperty('--shoot-angle', `${angle}deg`);
|
||||||
streak.style.transformOrigin = isFromLeft ? 'left center' : 'right center';
|
|
||||||
|
|
||||||
const topStart = Math.random() * 50;
|
const topStart = Math.random() * 50;
|
||||||
streak.style.setProperty('--shoot-start-x', isFromLeft ? '-20vw' : '120vw');
|
streak.style.left = isFromLeft ? '-20vw' : '120vw';
|
||||||
streak.style.setProperty('--shoot-end-x', isFromLeft ? '120vw' : '-20vw');
|
streak.style.top = `${topStart}vh`;
|
||||||
streak.style.setProperty('--shoot-start-y', `${topStart}vh`);
|
|
||||||
streak.style.setProperty('--shoot-end-y', `${topStart + 140}vh`); // 140vh drop to cross screen diagonally
|
// Travel 200 viewport widths exactly along the rotated angle
|
||||||
|
streak.style.setProperty('--shoot-distance', '200vw');
|
||||||
|
|
||||||
|
streak.style.animationDelay = `${Math.random() * 20}s`;
|
||||||
|
|
||||||
|
// MARK: Shooting Star Speed
|
||||||
|
const flightCycleDuration = Math.random() * 10 + 15; // 15-25s
|
||||||
|
streak.style.animationDuration = `${flightCycleDuration}s`;
|
||||||
|
|
||||||
streak.style.animationDelay = `${Math.random() * 20}s`; // Less frequent
|
|
||||||
streak.style.animationDuration = `${Math.random() * 2 + 3}s`; // 3-5s
|
|
||||||
container.appendChild(streak);
|
container.appendChild(streak);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useRandomDuration = enableDifferentDuration !== false;
|
const useRandomDuration = enableDifferentDuration !== false;
|
||||||
|
|
||||||
for (let i = 0; i < finalCount; i++) {
|
function createSpaceItem(imageArr, cCount, addedClass) {
|
||||||
|
for (let i = 0; i < cCount; i++) {
|
||||||
let symbol = document.createElement('div');
|
let symbol = document.createElement('div');
|
||||||
|
|
||||||
const randomImage = spaceImages[Math.floor(Math.random() * spaceImages.length)];
|
const randomImage = imageArr[Math.floor(Math.random() * imageArr.length)];
|
||||||
symbol.className = `space-symbol`;
|
symbol.className = `space-symbol ${addedClass}`;
|
||||||
|
|
||||||
let img = document.createElement('img');
|
let img = document.createElement('img');
|
||||||
img.src = randomImage;
|
img.src = randomImage;
|
||||||
img.onerror = function() {
|
img.onerror = function() {
|
||||||
this.style.display = 'none';
|
this.style.display = 'none';
|
||||||
}; // removed emoji fallback
|
};
|
||||||
symbol.appendChild(img);
|
symbol.appendChild(img);
|
||||||
|
|
||||||
const topPos = Math.random() * 90; // 0 to 90vh
|
const topPos = Math.random() * 90; // 0 to 90vh
|
||||||
const delaySeconds = Math.random() * 10;
|
|
||||||
|
|
||||||
// Zero gravity sizes / speeds
|
// Zero gravity sizes / speeds
|
||||||
const depth = Math.random();
|
const depth = Math.random();
|
||||||
const distanceScale = 0.3 + (depth * 0.7); // 0.3 to 1.0 (decently small)
|
// Make background elements (depth close to 0) much smaller than foreground
|
||||||
const blurAmount = depth < 0.3 ? (1 - depth) * 2 : 0;
|
const distanceScale = 0.15 + (depth * 0.85); // 0.15 to 1.0
|
||||||
|
|
||||||
symbol.style.filter = `blur(${blurAmount}px)`;
|
|
||||||
symbol.style.zIndex = Math.floor(depth * 30) + 20;
|
symbol.style.zIndex = Math.floor(depth * 30) + 20;
|
||||||
|
|
||||||
let durationSeconds = 30; // Very slow
|
let durationSeconds = 30; // Very slow
|
||||||
@@ -178,14 +221,20 @@ function createSpace() {
|
|||||||
|
|
||||||
if (goRight) {
|
if (goRight) {
|
||||||
symbol.style.animationName = 'space-drift-right';
|
symbol.style.animationName = 'space-drift-right';
|
||||||
symbol.style.left = '-15vw';
|
symbol.style.left = '-20vw';
|
||||||
|
symbol.style.right = 'auto';
|
||||||
} else {
|
} else {
|
||||||
symbol.style.animationName = 'space-drift-left';
|
symbol.style.animationName = 'space-drift-left';
|
||||||
symbol.style.right = '-15vw';
|
symbol.style.right = '-20vw';
|
||||||
|
symbol.style.left = 'auto';
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol.style.top = `${topPos}vh`;
|
symbol.style.top = `${topPos}vh`;
|
||||||
symbol.style.animationDuration = `${durationSeconds}s`;
|
symbol.style.animationDuration = `${durationSeconds}s`;
|
||||||
|
|
||||||
|
// Negative delay correctly scatters them initially across the screen
|
||||||
|
// so they don't all appear to spawn from the edge at the start
|
||||||
|
const delaySeconds = -(Math.random() * durationSeconds);
|
||||||
symbol.style.animationDelay = `${delaySeconds}s`;
|
symbol.style.animationDelay = `${delaySeconds}s`;
|
||||||
|
|
||||||
// Slow rotation inside inner div
|
// Slow rotation inside inner div
|
||||||
@@ -194,16 +243,33 @@ function createSpace() {
|
|||||||
const spinReverse = Math.random() > 0.5 ? 'reverse' : 'normal';
|
const spinReverse = Math.random() > 0.5 ? 'reverse' : 'normal';
|
||||||
rotationDiv.style.animation = `space-slow-spin ${rotDur}s linear infinite ${spinReverse}`;
|
rotationDiv.style.animation = `space-slow-spin ${rotDur}s linear infinite ${spinReverse}`;
|
||||||
|
|
||||||
rotationDiv.appendChild(symbol.cloneNode(true));
|
// Apply final static scaling and facing to inner image directly
|
||||||
|
img.style.transform = `scale(${distanceScale}) ${baseTransformScale}`;
|
||||||
|
|
||||||
// Apply final static scaling and facing to inner image
|
rotationDiv.appendChild(img);
|
||||||
rotationDiv.firstChild.style.transform = `scale(${distanceScale}) ${baseTransformScale}`;
|
|
||||||
|
|
||||||
symbol.innerHTML = '';
|
|
||||||
symbol.appendChild(rotationDiv);
|
symbol.appendChild(rotationDiv);
|
||||||
|
|
||||||
|
// Swap to a random image from the pool every time it completes an orbit (disappears)
|
||||||
|
if (imageArr.length > 1) {
|
||||||
|
// The animation delay pushes the initial cycle, so we use setInterval matched to duration
|
||||||
|
setInterval(() => {
|
||||||
|
// Update only if currently out of bounds to avoid popping
|
||||||
|
const rect = symbol.getBoundingClientRect();
|
||||||
|
if (rect.right < 0 || rect.left > window.innerWidth) {
|
||||||
|
img.src = imageArr[Math.floor(Math.random() * imageArr.length)];
|
||||||
|
}
|
||||||
|
}, 2000); // Check occasionally if it's off screen
|
||||||
|
}
|
||||||
|
|
||||||
container.appendChild(symbol);
|
container.appendChild(symbol);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createSpaceItem(planetImages, pCount, 'space-planet');
|
||||||
|
createSpaceItem(astronautImages, aCount, 'space-astronaut');
|
||||||
|
createSpaceItem(satelliteImages, sCount, 'space-satellite');
|
||||||
|
createSpaceItem([issImage], iCount, 'space-iss');
|
||||||
|
createSpaceItem(rocketImages, rCount, 'space-rocket');
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeSpace() {
|
function initializeSpace() {
|
||||||
|
|||||||
@@ -13,8 +13,6 @@
|
|||||||
.sports-symbol {
|
.sports-symbol {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10vh;
|
top: -10vh;
|
||||||
/* Default is empty, assigned in JS */
|
|
||||||
font-size: 3rem; /* Fallback emoji size */
|
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const symbolCount = config.SymbolCount || 5;
|
|||||||
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
const useRandomSymbols = config.EnableRandomSymbols !== undefined ? config.EnableRandomSymbols : true;
|
||||||
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
const enableRandomMobile = config.EnableRandomSymbolsMobile !== undefined ? config.EnableRandomSymbolsMobile : false;
|
||||||
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
||||||
|
const enableTrophy = config.EnableTrophy !== undefined ? config.EnableTrophy : false;
|
||||||
|
|
||||||
// Pre-declare and manage image assets
|
// Pre-declare and manage image assets
|
||||||
const SPORTS_ASSETS = {
|
const SPORTS_ASSETS = {
|
||||||
@@ -74,14 +75,11 @@ function createSports() {
|
|||||||
turf.style.background = `linear-gradient(180deg, transparent 0%, ${turfColorHex}4D 30%, ${turfColorHex}CC 100%)`;
|
turf.style.background = `linear-gradient(180deg, transparent 0%, ${turfColorHex}4D 30%, ${turfColorHex}CC 100%)`;
|
||||||
container.appendChild(turf);
|
container.appendChild(turf);
|
||||||
|
|
||||||
const standardCount = 15;
|
|
||||||
const totalSymbols = symbolCount + standardCount;
|
|
||||||
|
|
||||||
let isMobile = window.matchMedia("only screen and (max-width: 768px)").matches;
|
let isMobile = window.matchMedia("only screen and (max-width: 768px)").matches;
|
||||||
let finalCount = totalSymbols;
|
let ballsPerCategory = symbolCount;
|
||||||
|
|
||||||
if (isMobile) {
|
if (isMobile && !enableRandomMobile) {
|
||||||
finalCount = enableRandomMobile ? totalSymbols : standardCount;
|
ballsPerCategory = Math.min(symbolCount, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useRandomDuration = enableDifferentDuration !== false;
|
const useRandomDuration = enableDifferentDuration !== false;
|
||||||
@@ -90,36 +88,9 @@ function createSports() {
|
|||||||
const rawSportsBalls = config.SportsBalls || 'football,basketball,tennis,volleyball';
|
const rawSportsBalls = config.SportsBalls || 'football,basketball,tennis,volleyball';
|
||||||
const chosenCategories = rawSportsBalls.split(',').map(s => s.trim()).filter(s => s !== '');
|
const chosenCategories = rawSportsBalls.split(',').map(s => s.trim()).filter(s => s !== '');
|
||||||
|
|
||||||
// Assemble activeItems from categories
|
const createBall = (randomItem) => {
|
||||||
let activeItems = [];
|
|
||||||
chosenCategories.forEach(category => {
|
|
||||||
if (SPORTS_ASSETS[category]) {
|
|
||||||
activeItems.push(...SPORTS_ASSETS[category]);
|
|
||||||
} else {
|
|
||||||
// Legacy fallback (in case older explicit filenames remain in config string)
|
|
||||||
activeItems.push(category);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (activeItems.length === 0) activeItems.push(...SPORTS_ASSETS['football']); // fallback
|
|
||||||
|
|
||||||
// Track items we still need to show at least once
|
|
||||||
let guaranteedItems = [...activeItems];
|
|
||||||
|
|
||||||
// Create falling sports balls
|
|
||||||
for (let i = 0; i < finalCount; i++) {
|
|
||||||
let symbol = document.createElement('div');
|
let symbol = document.createElement('div');
|
||||||
|
|
||||||
// Pick a guaranteed ball first, otherwise pick completely randomly
|
|
||||||
let randomItem;
|
|
||||||
if (guaranteedItems.length > 0) {
|
|
||||||
const index = Math.floor(Math.random() * guaranteedItems.length);
|
|
||||||
randomItem = guaranteedItems[index];
|
|
||||||
guaranteedItems.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
randomItem = activeItems[Math.floor(Math.random() * activeItems.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
symbol.className = `sports-symbol sports-${randomItem}`;
|
symbol.className = `sports-symbol sports-${randomItem}`;
|
||||||
|
|
||||||
// Create inner div for spinning rotation
|
// Create inner div for spinning rotation
|
||||||
@@ -130,8 +101,7 @@ function createSports() {
|
|||||||
let img = document.createElement('img');
|
let img = document.createElement('img');
|
||||||
img.src = `../Seasonals/Resources/sport_assets/${randomItem}.png`;
|
img.src = `../Seasonals/Resources/sport_assets/${randomItem}.png`;
|
||||||
img.onerror = function() {
|
img.onerror = function() {
|
||||||
this.style.display = 'none'; // hide broken image icon
|
symbol.remove();
|
||||||
this.parentElement.innerHTML = getEmojiFallback(randomItem); // inject emoji fallback
|
|
||||||
};
|
};
|
||||||
innerDiv.appendChild(img);
|
innerDiv.appendChild(img);
|
||||||
|
|
||||||
@@ -140,6 +110,7 @@ function createSports() {
|
|||||||
symbol.style.animationIterationCount = 'infinite';
|
symbol.style.animationIterationCount = 'infinite';
|
||||||
innerDiv.style.animationName = 'sports-spin';
|
innerDiv.style.animationName = 'sports-spin';
|
||||||
innerDiv.style.animationIterationCount = 'infinite';
|
innerDiv.style.animationIterationCount = 'infinite';
|
||||||
|
innerDiv.style.animationTimingFunction = 'linear';
|
||||||
|
|
||||||
symbol.appendChild(innerDiv);
|
symbol.appendChild(innerDiv);
|
||||||
|
|
||||||
@@ -164,7 +135,19 @@ function createSports() {
|
|||||||
symbol.style.animationDelay = `${delaySeconds}s`;
|
symbol.style.animationDelay = `${delaySeconds}s`;
|
||||||
|
|
||||||
container.appendChild(symbol);
|
container.appendChild(symbol);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create falling sports balls
|
||||||
|
chosenCategories.forEach(category => {
|
||||||
|
let variants = SPORTS_ASSETS[category];
|
||||||
|
if (!variants) variants = [category]; // Legacy fallback
|
||||||
|
|
||||||
|
for (let i = 0; i < ballsPerCategory; i++) {
|
||||||
|
// Pick a random variant
|
||||||
|
const randomItem = variants[Math.floor(Math.random() * variants.length)];
|
||||||
|
createBall(randomItem);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Create the periodic flying trophy arc
|
// Create the periodic flying trophy arc
|
||||||
function launchTrophy() {
|
function launchTrophy() {
|
||||||
@@ -224,7 +207,9 @@ function createSports() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Launch initial trophy after a short delay
|
// Launch initial trophy after a short delay
|
||||||
|
if (enableTrophy) {
|
||||||
setTimeout(launchTrophy, Math.random() * 5000 + 2000);
|
setTimeout(launchTrophy, Math.random() * 5000 + 2000);
|
||||||
|
}
|
||||||
|
|
||||||
// Add Germany Colored confetti (Black, Red, Gold)
|
// Add Germany Colored confetti (Black, Red, Gold)
|
||||||
const confettiColors = ['#000000', '#FF0000', '#FFCC00'];
|
const confettiColors = ['#000000', '#FF0000', '#FFCC00'];
|
||||||
@@ -272,20 +257,7 @@ function createSports() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmojiFallback(type) {
|
/* Removed legacy fallback logic */
|
||||||
if (type.includes('soccer') || type.includes('football')) return '⚽';
|
|
||||||
if (type.includes('baseball')) return '⚾';
|
|
||||||
if (type.includes('basketball')) return '🏀';
|
|
||||||
if (type.includes('billiard')) return '🎱';
|
|
||||||
if (type.includes('bowling')) return '🎳';
|
|
||||||
if (type.includes('golf')) return '⛳';
|
|
||||||
if (type.includes('rugby')) return '🏈';
|
|
||||||
if (type.includes('tennis')) return '🎾';
|
|
||||||
if (type.includes('volleyball')) return '🏐';
|
|
||||||
if (type.includes('badminton')) return '🏸';
|
|
||||||
if (type.includes('waterball')) return '🤽';
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeSports() {
|
function initializeSports() {
|
||||||
if (!sports) return;
|
if (!sports) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user