Add carnival animation effects with CSS and JavaScript implementation
This commit is contained in:
174
Jellyfin.Plugin.Seasonals/Web/carnival.js
Normal file
174
Jellyfin.Plugin.Seasonals/Web/carnival.js
Normal file
@@ -0,0 +1,174 @@
|
||||
const config = window.SeasonalsPluginConfig?.Carnival || {};
|
||||
|
||||
const carnival = config.EnableCarnival !== undefined ? config.EnableCarnival : true;
|
||||
const randomCarnival = config.EnableRandomCarnival !== undefined ? config.EnableRandomCarnival : true;
|
||||
const randomCarnivalMobile = config.EnableRandomCarnivalMobile !== undefined ? config.EnableRandomCarnivalMobile : false;
|
||||
const enableDifferentDuration = config.EnableDifferentDuration !== undefined ? config.EnableDifferentDuration : true;
|
||||
const enableSway = config.EnableCarnivalSway !== undefined ? config.EnableCarnivalSway : true;
|
||||
const carnivalCount = config.ObjectCount || 80;
|
||||
|
||||
let msgPrinted = false;
|
||||
|
||||
// function to check and control the carnival animation
|
||||
function toggleCarnival() {
|
||||
const carnivalContainer = document.querySelector('.carnival-container');
|
||||
if (!carnivalContainer) return;
|
||||
|
||||
const videoPlayer = document.querySelector('.videoPlayerContainer');
|
||||
const trailerPlayer = document.querySelector('.youtubePlayerContainer');
|
||||
const isDashboard = document.body.classList.contains('dashboardDocument');
|
||||
const hasUserMenu = document.querySelector('#app-user-menu');
|
||||
|
||||
// hide carnival if video/trailer player is active or dashboard is visible
|
||||
if (videoPlayer || trailerPlayer || isDashboard || hasUserMenu) {
|
||||
carnivalContainer.style.display = 'none'; // hide carnival
|
||||
if (!msgPrinted) {
|
||||
console.log('Carnival hidden');
|
||||
msgPrinted = true;
|
||||
}
|
||||
} else {
|
||||
carnivalContainer.style.display = 'block'; // show carnival
|
||||
if (msgPrinted) {
|
||||
console.log('Carnival visible');
|
||||
msgPrinted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// observe changes in the DOM
|
||||
const observer = new MutationObserver(toggleCarnival);
|
||||
|
||||
// start observation
|
||||
observer.observe(document.body, {
|
||||
childList: true, // observe adding/removing of child elements
|
||||
subtree: true, // observe all levels of the DOM tree
|
||||
attributes: true // observe changes to attributes (e.g. class changes)
|
||||
});
|
||||
|
||||
const confettiColors = [
|
||||
'#fce18a', '#ff726d', '#b48def', '#f4306d',
|
||||
'#36c5f0', '#2ccc5d', '#e9b31d', '#9b59b6',
|
||||
'#3498db', '#e74c3c', '#1abc9c', '#f1c40f'
|
||||
];
|
||||
|
||||
function createConfettiPiece(container, isInitial = false) {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.classList.add('carnival-wrapper');
|
||||
|
||||
let swayWrapper = wrapper;
|
||||
|
||||
if (enableSway) {
|
||||
swayWrapper = document.createElement('div');
|
||||
swayWrapper.classList.add('carnival-sway-wrapper');
|
||||
wrapper.appendChild(swayWrapper);
|
||||
}
|
||||
|
||||
const confetti = document.createElement('div');
|
||||
confetti.classList.add('carnival-confetti');
|
||||
|
||||
// Random color
|
||||
const color = confettiColors[Math.floor(Math.random() * confettiColors.length)];
|
||||
confetti.style.backgroundColor = color;
|
||||
|
||||
// Random shape
|
||||
const shape = Math.random();
|
||||
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');
|
||||
}
|
||||
|
||||
// Random position
|
||||
wrapper.style.left = `${Math.random() * 100}%`;
|
||||
|
||||
// Random dimensions
|
||||
if (!confetti.classList.contains('circle') && !confetti.classList.contains('square') && !confetti.classList.contains('triangle')) {
|
||||
const width = Math.random() * 5 + 4;
|
||||
const height = Math.random() * 6 + 8;
|
||||
confetti.style.width = `${width}px`;
|
||||
confetti.style.height = `${height}px`;
|
||||
}
|
||||
|
||||
// Animation settings
|
||||
const duration = Math.random() * 5 + 5;
|
||||
|
||||
let delay = 0;
|
||||
if (isInitial) {
|
||||
delay = -Math.random() * duration;
|
||||
} else {
|
||||
delay = Math.random() * 10;
|
||||
}
|
||||
|
||||
wrapper.style.animationDelay = `${delay}s`;
|
||||
wrapper.style.animationDuration = `${duration}s`;
|
||||
|
||||
if (enableSway) {
|
||||
// Random sway duration
|
||||
const swayDuration = Math.random() * 2 + 3; // 3-5s per cycle
|
||||
swayWrapper.style.animationDuration = `${swayDuration}s`;
|
||||
swayWrapper.style.animationDelay = `-${Math.random() * 5}s`;
|
||||
|
||||
// Random sway amplitude (using CSS variable for dynamic keyframe)
|
||||
// Sway between 30px and 100px
|
||||
const swayAmount = Math.random() * 70 + 30;
|
||||
const direction = Math.random() > 0.5 ? 1 : -1;
|
||||
swayWrapper.style.setProperty('--sway-amount', `${swayAmount * direction}px`);
|
||||
}
|
||||
|
||||
// Flutter speed variation
|
||||
confetti.style.animationDuration = `${Math.random() * 2 + 1}s`;
|
||||
|
||||
if (enableSway) {
|
||||
swayWrapper.appendChild(confetti);
|
||||
wrapper.appendChild(swayWrapper);
|
||||
} else {
|
||||
wrapper.appendChild(confetti);
|
||||
}
|
||||
|
||||
container.appendChild(wrapper);
|
||||
}
|
||||
|
||||
function addRandomCarnivalObjects(count) {
|
||||
const carnivalContainer = document.querySelector('.carnival-container');
|
||||
if (!carnivalContainer) return;
|
||||
|
||||
console.log('Adding random carnival confetti');
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
createConfettiPiece(carnivalContainer, true);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize standard carnival objects
|
||||
function initCarnivalObjects() {
|
||||
let container = document.querySelector('.carnival-container');
|
||||
if (!container) {
|
||||
container = document.createElement("div");
|
||||
container.className = "carnival-container";
|
||||
container.setAttribute("aria-hidden", "true");
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
// Initial confetti
|
||||
for (let i = 0; i < 30; i++) {
|
||||
createConfettiPiece(container, true);
|
||||
}
|
||||
}
|
||||
|
||||
// initialize carnival
|
||||
function initializeCarnival() {
|
||||
if (!carnival) return; // exit if carnival is disabled
|
||||
initCarnivalObjects();
|
||||
toggleCarnival();
|
||||
|
||||
const screenWidth = window.innerWidth;
|
||||
if (randomCarnival && (screenWidth > 768 || randomCarnivalMobile)) {
|
||||
addRandomCarnivalObjects(carnivalCount);
|
||||
}
|
||||
}
|
||||
|
||||
initializeCarnival();
|
||||
Reference in New Issue
Block a user