-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
122 lines (103 loc) · 4.69 KB
/
script.js
File metadata and controls
122 lines (103 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/* ─────────────────────────────────────────────
Portfolio Script v3 — Enoch Jackson C
───────────────────────────────────────────── */
// ── SEAMLESS TICKER ───────────────────────────
// Clone the track content once → total width = 2×
// CSS animates -50% which is exactly 1× → perfect loop
(function initTicker() {
const track = document.getElementById('ticker-track');
if (!track) return;
// Duplicate inner content
track.innerHTML += track.innerHTML;
// Scale speed to pixel width (px per second = 80)
const pxPerSec = 80;
const totalWidth = track.scrollWidth; // full 2× width
const duration = (totalWidth / 2) / pxPerSec; // time for 1× width
track.style.setProperty('--ticker-duration', duration + 's');
// Start animation only after clone is rendered
requestAnimationFrame(() => track.classList.add('running'));
})();
// ── NAV ACTIVE LINK ──────────────────────────
const navLinks = document.querySelectorAll('.nav-links a');
const sectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + entry.target.id) {
link.classList.add('active');
}
});
}
});
}, { threshold: 0.4, rootMargin: '-80px 0px 0px 0px' });
document.querySelectorAll('section[id]').forEach(s => sectionObserver.observe(s));
// ── HAMBURGER ────────────────────────────────
const hamburger = document.getElementById('hamburger');
const mobileMenu = document.getElementById('mobile-menu');
hamburger.addEventListener('click', () => {
const open = mobileMenu.classList.toggle('open');
hamburger.setAttribute('aria-expanded', open);
mobileMenu.setAttribute('aria-hidden', !open);
});
document.querySelectorAll('.mobile-link').forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.remove('open');
hamburger.setAttribute('aria-expanded', false);
mobileMenu.setAttribute('aria-hidden', true);
});
});
// ── FADE UP ON SCROLL ─────────────────────────
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -30px 0px' });
document.querySelectorAll('.fade-up').forEach(el => observer.observe(el));
// ── PROJECT FILTER ────────────────────────────
const filterBtns = document.querySelectorAll('.filter-btn');
const projectCards = document.querySelectorAll('.project-card');
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
// set active
filterBtns.forEach(b => { b.classList.remove('active'); b.setAttribute('aria-selected', false); });
btn.classList.add('active');
btn.setAttribute('aria-selected', true);
const filter = btn.dataset.filter;
projectCards.forEach(card => {
if (filter === 'all' || card.dataset.category === filter) {
card.classList.remove('hidden');
card.style.animation = 'fadeIn 0.35s ease forwards';
} else {
card.classList.add('hidden');
}
});
});
});
// ── SMOOTH TILT on PROJECT CARDS ──────────────
projectCards.forEach(card => {
card.addEventListener('mousemove', e => {
const r = card.getBoundingClientRect();
const x = ((e.clientX - r.left) / r.width - 0.5) * 5;
const y = ((e.clientY - r.top) / r.height - 0.5) * -5;
card.style.transform = `translateY(-5px) rotateX(${y}deg) rotateY(${x}deg)`;
card.style.perspective = '800px';
});
card.addEventListener('mouseleave', () => { card.style.transform = ''; });
});
// ── CURSOR GLOW EFFECT ────────────────────────
const orb = document.querySelector('.orb-2');
if (orb) {
window.addEventListener('mousemove', e => {
const x = (e.clientX / window.innerWidth) * 100;
const y = (e.clientY / window.innerHeight) * 100;
orb.style.transition = 'left 1.5s ease, top 1.5s ease';
orb.style.left = `calc(${x}% - 200px)`;
orb.style.top = `calc(${y}% - 200px)`;
orb.style.right = 'auto';
orb.style.bottom = 'auto';
}, { passive: true });
}