Skip to content

Commit 6ffc21f

Browse files
committed
style: enhance visual elements and animations in link page, including updated font links, improved avatar styling, and new shooting star effect
1 parent cf0fe64 commit 6ffc21f

1 file changed

Lines changed: 205 additions & 15 deletions

File tree

src/pages/link/index.astro

Lines changed: 205 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ import Layout from '../../layouts/Layout.astro';
1212
<link rel="preconnect" href="https://fonts.googleapis.com" />
1313
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1414
<link
15-
href="https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Noto+Sans+TC:wght@100..900&family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&family=Playball&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Satisfy&display=swap"
15+
href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"
1616
rel="stylesheet"
1717
/>
18+
<link rel="preload" as="image" href="https://github.com/AndyWang505/link/blob/main/img/Suisei03.png?raw=true" />
1819
<link
1920
rel="stylesheet"
20-
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
21+
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/brands.min.css"
22+
/>
23+
<link
24+
rel="stylesheet"
25+
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/solid.min.css"
26+
/>
27+
<link
28+
rel="stylesheet"
29+
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/fontawesome.min.css"
2130
/>
2231

2332
<style>
@@ -100,8 +109,9 @@ import Layout from '../../layouts/Layout.astro';
100109

101110
section {
102111
position: absolute;
103-
padding: 2rem;
112+
padding: 2.8rem 3rem;
104113
margin: 0 0.3rem;
114+
min-width: 340px;
105115
backdrop-filter: blur(10px);
106116
background-color: transparent;
107117
border: 1.5px solid rgb(230, 230, 230);
@@ -113,16 +123,51 @@ import Layout from '../../layouts/Layout.astro';
113123

114124
main.loaded section {
115125
opacity: 1;
126+
transition-delay: 1.2s;
127+
animation: float 6s ease-in-out infinite;
128+
animation-delay: 3s;
129+
}
130+
131+
@keyframes float {
132+
0%, 100% { transform: translateY(0); }
133+
50% { transform: translateY(-7px); }
116134
}
117135

118136
header {
119137
text-align: center;
120138
}
121139

140+
.avatar-wrap {
141+
position: relative;
142+
display: inline-block;
143+
margin-bottom: 0.5rem;
144+
}
145+
146+
.avatar-wrap::after {
147+
content: '';
148+
position: absolute;
149+
inset: -3px;
150+
border-radius: 9999px;
151+
border: 2px solid rgba(255, 255, 255, 0.55);
152+
animation: pulse-ring 3s ease-out infinite;
153+
}
154+
155+
@keyframes pulse-ring {
156+
0% { transform: scale(1); opacity: 0.6; }
157+
100% { transform: scale(1.4); opacity: 0; }
158+
}
159+
122160
header img {
123161
border-radius: 9999px;
124162
width: 6rem;
125163
height: 6rem;
164+
display: block;
165+
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.25), 0 0 18px rgba(255, 255, 255, 0.12);
166+
transition: box-shadow 0.3s ease;
167+
}
168+
169+
header img:hover {
170+
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.6), 0 0 28px rgba(255, 255, 255, 0.28);
126171
}
127172

128173
header h1 {
@@ -140,25 +185,96 @@ import Layout from '../../layouts/Layout.astro';
140185

141186
li {
142187
margin: 2rem auto;
188+
opacity: 0;
189+
transform: translateY(16px);
190+
}
191+
192+
main.loaded li {
193+
animation: slideInUp 0.5s ease forwards;
194+
}
195+
196+
main.loaded li:nth-child(1) { animation-delay: 1.5s; }
197+
main.loaded li:nth-child(2) { animation-delay: 1.62s; }
198+
main.loaded li:nth-child(3) { animation-delay: 1.74s; }
199+
main.loaded li:nth-child(4) { animation-delay: 1.86s; }
200+
main.loaded li:nth-child(5) { animation-delay: 1.98s; }
201+
202+
@keyframes slideInUp {
203+
to {
204+
opacity: 1;
205+
transform: translateY(0);
206+
}
143207
}
144208

145209
li a {
210+
position: relative;
211+
overflow: hidden;
146212
display: flex;
147213
align-items: center;
148214
justify-content: center;
215+
padding: 0.3rem 1.2rem;
216+
border-radius: 8px;
217+
transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
218+
}
219+
220+
li a::before {
221+
content: '';
222+
position: absolute;
223+
top: 0;
224+
left: -80%;
225+
width: 60%;
226+
height: 100%;
227+
background: linear-gradient(
228+
120deg,
229+
transparent 0%,
230+
rgba(255, 255, 255, 0.13) 50%,
231+
transparent 100%
232+
);
233+
transition: left 0s;
234+
pointer-events: none;
235+
}
236+
237+
li a:hover::before {
238+
left: 150%;
239+
transition: left 0.5s ease;
240+
}
241+
242+
li a:hover {
243+
transform: scale(1.04);
244+
background: rgba(255, 255, 255, 0.07);
245+
box-shadow: 0 0 18px rgba(255, 255, 255, 0.1);
149246
}
150247

151248
li h2 {
152249
padding: 0 0.6rem;
250+
transition: letter-spacing 0.3s ease, text-shadow 0.3s ease;
153251
}
154252

155253
li i {
156254
font-size: 26px;
255+
transition: text-shadow 0.3s ease;
256+
}
257+
258+
a:hover h2 {
259+
letter-spacing: 0.06em;
260+
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
157261
}
158262

159-
a:hover h2,
160263
a:hover i {
161-
color: rgb(95, 95, 95);
264+
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
265+
}
266+
267+
.ripple {
268+
position: absolute;
269+
border-radius: 9999px;
270+
background: rgba(255, 255, 255, 0.22);
271+
transform: scale(0);
272+
animation: ripple-expand 0.55s ease-out forwards;
273+
pointer-events: none;
274+
}
275+
276+
@keyframes ripple-expand {
277+
to { transform: scale(2.8); opacity: 0; }
162278
}
163279

164280
footer {
@@ -182,7 +298,9 @@ import Layout from '../../layouts/Layout.astro';
182298
/* Mobile responsive */
183299
@media (max-width: 480px) {
184300
section {
185-
padding: 2rem 1rem;
301+
padding: 2.2rem 1.4rem;
302+
min-width: unset;
303+
width: calc(100vw - 2rem);
186304
}
187305

188306
header p {
@@ -195,11 +313,15 @@ import Layout from '../../layouts/Layout.astro';
195313
<main>
196314
<section id="app">
197315
<header>
198-
<img
199-
src="https://avatars.githubusercontent.com/u/71600455?v=4"
200-
alt="head shot"
201-
loading="lazy"
202-
/>
316+
<div class="avatar-wrap">
317+
<img
318+
src="https://avatars.githubusercontent.com/u/71600455?v=4"
319+
alt="head shot"
320+
fetchpriority="high"
321+
width="96"
322+
height="96"
323+
/>
324+
</div>
203325
<h1>AndyWang</h1>
204326
<p class="address">
205327
<i class="fa-solid fa-location-dot"></i>Taiwan, Hsinchu City
@@ -296,31 +418,99 @@ import Layout from '../../layouts/Layout.astro';
296418
});
297419
}
298420

421+
// Shooting stars
422+
const shootingStars = [];
423+
424+
function createShootingStar() {
425+
return {
426+
x: Math.random() * canvas.width * 0.7,
427+
y: Math.random() * canvas.height * 0.4,
428+
length: Math.random() * 100 + 60,
429+
speed: Math.random() * 6 + 4,
430+
opacity: 1,
431+
angle: Math.PI / 5,
432+
active: true,
433+
};
434+
}
435+
436+
function drawShootingStars() {
437+
shootingStars.forEach((s, idx) => {
438+
if (!s.active) return;
439+
const dx = Math.cos(s.angle) * s.length;
440+
const dy = Math.sin(s.angle) * s.length;
441+
const grad = ctx.createLinearGradient(s.x, s.y, s.x - dx, s.y - dy);
442+
grad.addColorStop(0, `rgba(255, 255, 255, ${s.opacity})`);
443+
grad.addColorStop(1, 'rgba(255, 255, 255, 0)');
444+
ctx.beginPath();
445+
ctx.moveTo(s.x, s.y);
446+
ctx.lineTo(s.x - dx, s.y - dy);
447+
ctx.strokeStyle = grad;
448+
ctx.lineWidth = 1.5;
449+
ctx.stroke();
450+
s.x += Math.cos(s.angle) * s.speed;
451+
s.y += Math.sin(s.angle) * s.speed;
452+
s.opacity -= 0.018;
453+
if (s.opacity <= 0 || s.x > canvas.width || s.y > canvas.height) {
454+
shootingStars.splice(idx, 1);
455+
}
456+
});
457+
}
458+
459+
// Spawn a shooting star every 2.5–5 seconds
460+
function scheduleShootingStar() {
461+
shootingStars.push(createShootingStar());
462+
setTimeout(scheduleShootingStar, Math.random() * 2500 + 2500);
463+
}
464+
scheduleShootingStar();
465+
299466
function animate() {
300467
drawStars();
468+
drawShootingStars();
301469
updateStars();
302470
requestAnimationFrame(animate);
303471
}
304472

305473
animate(); // Start the animation
306474

307-
// Adjust canvas size on window resize
475+
// Adjust canvas size on window resize (debounced)
476+
let resizeTimer;
308477
window.addEventListener('resize', () => {
309-
canvas.width = window.innerWidth;
310-
canvas.height = window.innerHeight;
478+
clearTimeout(resizeTimer);
479+
resizeTimer = setTimeout(() => {
480+
canvas.width = window.innerWidth;
481+
canvas.height = window.innerHeight;
482+
}, 100);
311483
});
312484

313-
// Background image fade-in effect
485+
// Background image fade-in effect with fallback timeout
314486
const mainElement = document.querySelector('main');
315487
if (mainElement) {
316488
const backgroundImage = new Image();
317489
backgroundImage.src =
318490
'https://github.com/AndyWang505/link/blob/main/img/Suisei03.png?raw=true';
319491

492+
const fallback = setTimeout(() => mainElement.classList.add('loaded'), 3000);
493+
320494
backgroundImage.onload = () => {
495+
clearTimeout(fallback);
321496
mainElement.classList.add('loaded');
322497
};
323498
}
499+
500+
// Ripple effect on link click
501+
document.querySelectorAll('li a').forEach((link) => {
502+
link.addEventListener('click', function (e) {
503+
const ripple = document.createElement('span');
504+
ripple.classList.add('ripple');
505+
const rect = this.getBoundingClientRect();
506+
const size = Math.max(rect.width, rect.height);
507+
ripple.style.width = ripple.style.height = size + 'px';
508+
ripple.style.left = e.clientX - rect.left - size / 2 + 'px';
509+
ripple.style.top = e.clientY - rect.top - size / 2 + 'px';
510+
this.appendChild(ripple);
511+
ripple.addEventListener('animationend', () => ripple.remove());
512+
});
513+
});
324514
</script>
325515
</body>
326516
</html>

0 commit comments

Comments
 (0)