@@ -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