- Set up ESLint and Prettier for code quality - Split large script.js into modular architecture (DOM, animations, effects, easter-eggs, sound, interactions) - Organize assets into proper directory structure (assets/css, assets/js/modules, assets/images) - Add semantic HTML5 landmarks (header, main, nav, footer) - Implement ARIA labels and keyboard navigation for accessibility - Set up Vite build system with minification and optimization - Add CSS custom properties for design tokens - Create sitemap.xml and robots.txt for SEO - Add MIT LICENSE - Expand README with comprehensive documentation - Set up GitHub Actions CI/CD workflow - Optimize build output: ~59KB total (30KB image + 13KB CSS + 16KB JS gzipped) Co-authored-by: ZaneThePython <102631678+ZaneThePython@users.noreply.github.com>
123 lines
2.9 KiB
JavaScript
123 lines
2.9 KiB
JavaScript
// Animation utilities and functions
|
|
import { DOM, getMainElements } from './dom.js';
|
|
|
|
// Animate elements on page load
|
|
export function animateOnLoad() {
|
|
const { avatar, brandName, disclaimer } = getMainElements();
|
|
const navButtons = DOM.getAll('.nav-button');
|
|
|
|
// Set initial states
|
|
if (avatar) {
|
|
avatar.style.opacity = '0';
|
|
avatar.style.transform = 'translateY(30px)';
|
|
}
|
|
if (brandName) {
|
|
brandName.style.opacity = '0';
|
|
brandName.style.transform = 'translateY(30px)';
|
|
}
|
|
|
|
navButtons.forEach((button, index) => {
|
|
button.style.opacity = '0';
|
|
button.style.transform = 'translateX(30px)';
|
|
button.style.transitionDelay = `${index * 0.1}s`;
|
|
});
|
|
|
|
if (disclaimer) {
|
|
disclaimer.style.opacity = '0';
|
|
}
|
|
|
|
// Animate in sequence
|
|
setTimeout(() => {
|
|
if (avatar) {
|
|
avatar.style.transition = 'all 0.8s ease';
|
|
avatar.style.opacity = '1';
|
|
avatar.style.transform = 'translateY(0)';
|
|
}
|
|
}, 200);
|
|
|
|
setTimeout(() => {
|
|
if (brandName) {
|
|
brandName.style.transition = 'all 0.8s ease';
|
|
brandName.style.opacity = '1';
|
|
brandName.style.transform = 'translateY(0)';
|
|
}
|
|
}, 400);
|
|
|
|
setTimeout(() => {
|
|
navButtons.forEach((button) => {
|
|
button.style.transition = 'all 0.6s ease';
|
|
button.style.opacity = '1';
|
|
button.style.transform = 'translateX(0)';
|
|
});
|
|
}, 600);
|
|
|
|
setTimeout(() => {
|
|
if (disclaimer) {
|
|
disclaimer.style.transition = 'all 0.8s ease';
|
|
disclaimer.style.opacity = '1';
|
|
}
|
|
}, 800);
|
|
}
|
|
|
|
// Typing animation
|
|
export function typeWriter(element, text, speed = 100) {
|
|
if (!element) return;
|
|
let i = 0;
|
|
element.innerHTML = '';
|
|
|
|
function type() {
|
|
if (i < text.length) {
|
|
element.innerHTML += text.charAt(i);
|
|
i++;
|
|
setTimeout(type, speed);
|
|
}
|
|
}
|
|
|
|
type();
|
|
}
|
|
|
|
// Animate skill tags with stagger effect
|
|
export function animateSkillTags() {
|
|
const skillTags = DOM.getAll('.skill-tag');
|
|
|
|
skillTags.forEach((tag, index) => {
|
|
tag.style.opacity = '0';
|
|
tag.style.transform = 'translateY(20px)';
|
|
|
|
setTimeout(() => {
|
|
tag.style.transition = 'all 0.5s ease';
|
|
tag.style.opacity = '1';
|
|
tag.style.transform = 'translateY(0)';
|
|
}, index * 100);
|
|
});
|
|
}
|
|
|
|
// Animate project cards with stagger effect
|
|
export function animateProjectCards() {
|
|
const projectCards = DOM.getAll('.project-card');
|
|
|
|
projectCards.forEach((card, index) => {
|
|
card.style.opacity = '0';
|
|
card.style.transform = 'translateY(30px)';
|
|
|
|
setTimeout(() => {
|
|
card.style.transition = 'all 0.6s ease';
|
|
card.style.opacity = '1';
|
|
card.style.transform = 'translateY(0)';
|
|
}, index * 150);
|
|
});
|
|
}
|
|
|
|
// Add typing animation for tagline
|
|
export function addTypingAnimation() {
|
|
const { tagline } = getMainElements();
|
|
if (tagline) {
|
|
const originalText = tagline.textContent;
|
|
tagline.textContent = '';
|
|
|
|
setTimeout(() => {
|
|
typeWriter(tagline, originalText, 100);
|
|
}, 2000);
|
|
}
|
|
}
|