AI_PPT / main.js
Dp1412's picture
Upload 3 files
81f38b0 verified
Raw
History Blame Contribute Delete
5.35 kB
/**
* Stay Fresh in AI β€” Presentation Engine
* Pure JavaScript β€” No Dependencies
*/
(function () {
'use strict';
// ─── State ───
let currentSlide = 1;
const totalSlides = 10;
let isAnimating = false;
// ─── DOM Elements ───
const progressFill = document.getElementById('progressFill');
const currentSlideEl = document.getElementById('currentSlide');
const totalSlidesEl = document.getElementById('totalSlides');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const slides = document.querySelectorAll('.slide');
// ─── Init ───
function init() {
totalSlidesEl.textContent = totalSlides;
updateUI(1);
bindEvents();
handleHash();
}
// ─── Event Binding ───
function bindEvents() {
prevBtn.addEventListener('click', goToPrev);
nextBtn.addEventListener('click', goToNext);
document.addEventListener('keydown', handleKeyDown);
// Touch swipe
let touchStartX = 0;
document.addEventListener('touchstart', e => {
touchStartX = e.changedTouches[0].screenX;
}, { passive: true });
document.addEventListener('touchend', e => {
const diff = touchStartX - e.changedTouches[0].screenX;
if (Math.abs(diff) > 50) {
diff > 0 ? goToNext() : goToPrev();
}
}, { passive: true });
// Mouse wheel (throttled)
let wheelLock = false;
document.addEventListener('wheel', e => {
if (wheelLock) return;
wheelLock = true;
setTimeout(() => { wheelLock = false; }, 800);
e.deltaY > 0 ? goToNext() : goToPrev();
}, { passive: true });
}
// ─── Keyboard Handler ───
function handleKeyDown(e) {
switch (e.key) {
case 'ArrowRight':
case ' ':
case 'Enter':
e.preventDefault();
goToNext();
break;
case 'ArrowLeft':
case 'Backspace':
e.preventDefault();
goToPrev();
break;
case 'Home':
e.preventDefault();
goToSlide(1);
break;
case 'End':
e.preventDefault();
goToSlide(totalSlides);
break;
}
// Number keys 1–9 for quick jump
const num = parseInt(e.key);
if (!isNaN(num) && num >= 1 && num <= Math.min(totalSlides, 9)) {
goToSlide(num);
}
}
// ─── Navigation ───
function goToNext() {
if (currentSlide < totalSlides && !isAnimating) goToSlide(currentSlide + 1);
}
function goToPrev() {
if (currentSlide > 1 && !isAnimating) goToSlide(currentSlide - 1);
}
function goToSlide(n) {
if (n === currentSlide || n < 1 || n > totalSlides || isAnimating) return;
isAnimating = true;
currentSlide = n;
slides.forEach((slide, i) => {
const num = i + 1;
slide.classList.remove('active', 'prev');
if (num === currentSlide) {
slide.classList.add('active');
triggerAnimations(slide);
} else if (num < currentSlide) {
slide.classList.add('prev');
}
});
updateUI(currentSlide);
setTimeout(() => { isAnimating = false; }, 500);
}
// ─── UI Update ───
function updateUI(n) {
currentSlideEl.textContent = n;
progressFill.style.width = `${(n / totalSlides) * 100}%`;
prevBtn.style.opacity = n === 1 ? '0.3' : '1';
prevBtn.style.pointerEvents = n === 1 ? 'none' : 'auto';
nextBtn.style.opacity = n === totalSlides ? '0.3' : '1';
nextBtn.style.pointerEvents = n === totalSlides ? 'none' : 'auto';
history.replaceState(null, null, `#slide-${n}`);
}
// ─── Re-trigger CSS animations when a slide becomes active ───
function triggerAnimations(slide) {
const els = slide.querySelectorAll('.agenda-item, [style*="--delay"]');
els.forEach(el => {
el.style.animation = 'none';
void el.offsetHeight; // reflow
el.style.animation = null;
});
}
// ─── URL Hash Support ───
function handleHash() {
const hash = window.location.hash;
if (hash && hash.startsWith('#slide-')) {
const n = parseInt(hash.replace('#slide-', ''));
if (n >= 1 && n <= totalSlides) goToSlide(n);
}
}
window.addEventListener('hashchange', handleHash);
// ─── Public API ───
window.PresentationAPI = {
next: goToNext,
prev: goToPrev,
goTo: goToSlide,
getCurrentSlide: () => currentSlide,
getTotalSlides: () => totalSlides
};
// ─── Boot ───
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();