- Created .gitignore to exclude unnecessary files. - Added MIT License for project licensing. - Introduced README.md and README.zh-CN.md for documentation in English and Chinese. - Implemented web design engineer skill with detailed workflow and design principles. - Included advanced patterns and code templates for reference in the skill. - Added demo HTML files showcasing the skill's capabilities.
133 lines
3.8 KiB
JavaScript
133 lines
3.8 KiB
JavaScript
// Sticky nav background on scroll
|
|
const nav = document.getElementById('nav');
|
|
const onScroll = () => {
|
|
if (window.scrollY > 60) nav.classList.add('is-scrolled');
|
|
else nav.classList.remove('is-scrolled');
|
|
};
|
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
onScroll();
|
|
|
|
// Mobile burger (toggles a quick overlay menu)
|
|
const burger = document.getElementById('burger');
|
|
if (burger) {
|
|
burger.addEventListener('click', () => {
|
|
const menu = document.querySelector('.nav__menu');
|
|
if (!menu) return;
|
|
const open = menu.style.display === 'flex';
|
|
menu.style.display = open ? '' : 'flex';
|
|
menu.style.flexDirection = 'column';
|
|
menu.style.position = 'absolute';
|
|
menu.style.top = '100%';
|
|
menu.style.left = '0';
|
|
menu.style.right = '0';
|
|
menu.style.padding = open ? '' : '24px';
|
|
menu.style.background = open ? '' : 'rgba(245,242,236,.96)';
|
|
menu.style.color = open ? '' : '#1a1a1a';
|
|
menu.style.backdropFilter = 'blur(12px)';
|
|
});
|
|
}
|
|
|
|
// Works filter
|
|
const chips = document.querySelectorAll('.chip');
|
|
const cards = document.querySelectorAll('.grid .card');
|
|
chips.forEach(chip => {
|
|
chip.addEventListener('click', () => {
|
|
chips.forEach(c => c.classList.remove('is-active'));
|
|
chip.classList.add('is-active');
|
|
const filter = chip.dataset.filter;
|
|
cards.forEach(card => {
|
|
const cat = card.dataset.cat;
|
|
const show = filter === 'all' || cat === filter;
|
|
card.style.display = show ? '' : 'none';
|
|
});
|
|
});
|
|
});
|
|
|
|
// Reveal-on-scroll
|
|
const revealTargets = document.querySelectorAll(
|
|
'.section__head, .card, .about__media, .about__text, .service, .post, .quote blockquote, .contact__inner'
|
|
);
|
|
revealTargets.forEach(el => el.classList.add('reveal'));
|
|
|
|
const io = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('is-in');
|
|
io.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.12, rootMargin: '0px 0px -40px 0px' });
|
|
|
|
revealTargets.forEach(el => io.observe(el));
|
|
|
|
// Init AI Canvas Background for Hero
|
|
initAICanvas();
|
|
|
|
function initAICanvas() {
|
|
const canvas = document.getElementById('ai-canvas');
|
|
if (!canvas) return;
|
|
const ctx = canvas.getContext('2d');
|
|
let width, height;
|
|
|
|
function resize() {
|
|
width = canvas.width = window.innerWidth;
|
|
height = canvas.height = window.innerHeight;
|
|
}
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
const particles = [];
|
|
const particleCount = window.innerWidth > 768 ? 120 : 60;
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
particles.push({
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
vx: (Math.random() - 0.5) * 0.5,
|
|
vy: (Math.random() - 0.5) * 0.5,
|
|
size: Math.random() * 2 + 0.5
|
|
});
|
|
}
|
|
|
|
function draw() {
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
// Draw lines
|
|
ctx.lineWidth = 0.5;
|
|
for (let i = 0; i < particleCount; i++) {
|
|
for (let j = i + 1; j < particleCount; j++) {
|
|
const dx = particles[i].x - particles[j].x;
|
|
const dy = particles[i].y - particles[j].y;
|
|
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
if (dist < 150) {
|
|
ctx.strokeStyle = `rgba(0, 240, 255, ${0.2 - dist/150*0.2})`;
|
|
ctx.beginPath();
|
|
ctx.moveTo(particles[i].x, particles[i].y);
|
|
ctx.lineTo(particles[j].x, particles[j].y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw particles
|
|
ctx.fillStyle = 'rgba(0, 240, 255, 0.6)';
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const p = particles[i];
|
|
p.x += p.vx;
|
|
p.y += p.vy;
|
|
|
|
if (p.x < 0 || p.x > width) p.vx *= -1;
|
|
if (p.y < 0 || p.y > height) p.vy *= -1;
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
|
|
requestAnimationFrame(draw);
|
|
}
|
|
|
|
draw();
|
|
}
|