-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
128 lines (105 loc) · 3.46 KB
/
Copy pathscript.js
File metadata and controls
128 lines (105 loc) · 3.46 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
123
124
125
126
127
128
// ---------- Mobile nav toggle ----------
const hamburger = document.querySelector('.hamburger');
const navLinks = document.querySelector('.nav-links');
if (hamburger && navLinks) {
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active');
navLinks.classList.toggle('nav-active');
});
navLinks.querySelectorAll('a').forEach((link) => {
link.addEventListener('click', () => {
hamburger.classList.remove('active');
navLinks.classList.remove('nav-active');
});
});
}
// ---------- Typing effect (hero role cycler) ----------
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const roleEl = document.getElementById('role-text');
const roles = [
'Building Production-Grade Mobile Apps',
'Clean Architecture & Firebase',
'Full-Stack Flutter Ecosystems',
'Open to Remote Opportunities'
];
if (roleEl) {
if (prefersReducedMotion) {
roleEl.textContent = roles[0];
} else {
let roleIndex = 0;
let charIndex = 0;
let deleting = false;
function typeLoop() {
const current = roles[roleIndex];
roleEl.textContent = current.substring(0, charIndex);
let speed = deleting ? 35 : 55;
if (!deleting && charIndex === current.length) {
speed = 1400;
deleting = true;
} else if (deleting && charIndex === 0) {
deleting = false;
roleIndex = (roleIndex + 1) % roles.length;
speed = 400;
} else {
charIndex += deleting ? -1 : 1;
}
setTimeout(typeLoop, speed);
}
typeLoop();
}
}
// ---------- Scroll reveal ----------
const revealTargets = document.querySelectorAll('.project-card, .timeline-item');
if (prefersReducedMotion) {
revealTargets.forEach((el) => el.classList.add('revealed'));
} else if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => entry.target.classList.add('revealed'), i * 60);
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.15 }
);
revealTargets.forEach((el) => observer.observe(el));
} else {
revealTargets.forEach((el) => el.classList.add('revealed'));
}
// ---------- Active nav link ----------
const currentPage = window.location.pathname.split('/').pop() || 'index.html';
document.querySelectorAll('.nav-links a').forEach((link) => {
if (link.getAttribute('href') === currentPage) {
link.classList.add('active');
}
});
const handleMouseMove = (e) => {
const target = e.currentTarget;
const rect = target.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
target.style.setProperty("--mouse-x", `${x}px`);
target.style.setProperty("--mouse-y", `${y}px`);
};
const interactiveCards = document.querySelectorAll(".service-card, .project-card");
if (!prefersReducedMotion) {
for (const card of interactiveCards) {
card.addEventListener("mousemove", handleMouseMove);
}
}
// ---------- Page Transitions ----------
const transitionLinks = document.querySelectorAll('a[href$=".html"]');
if (!prefersReducedMotion) {
transitionLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetUrl = link.getAttribute('href');
document.body.classList.add('fade-out');
setTimeout(() => {
window.location.href = targetUrl;
}, 400);
});
});
}