From 9921d24910c8e3c9143b10e13a0788b70e96046a Mon Sep 17 00:00:00 2001 From: biss Date: Sat, 20 Jun 2026 10:37:40 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E5=8A=A8=E7=94=BB=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/random.js | 75 ++++++- src/components/CustomContextMenu.astro | 5 + src/styles/site.css | 300 +++++++++++++++++++++++++ 3 files changed, 378 insertions(+), 2 deletions(-) diff --git a/public/js/random.js b/public/js/random.js index 481ee1a..91d37b5 100644 --- a/public/js/random.js +++ b/public/js/random.js @@ -1,4 +1,72 @@ -function randomPost() { +function randomPost(trigger) { + const source = trigger instanceof Element + ? trigger + : document.activeElement?.closest?.('.nav-random, [onclick*="randomPost"]'); + const overlay = document.createElement('div'); + const reduceMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches; + + overlay.className = 'random-post-transition'; + overlay.setAttribute('role', 'status'); + overlay.setAttribute('aria-live', 'polite'); + overlay.innerHTML = ` + +
+ + \u6b63\u5728\u62bd\u53d6\u4e00\u7bc7\u6587\u7ae0 +
+ `; + + source?.classList.add('is-randomizing'); + source?.setAttribute('aria-busy', 'true'); + document.body.appendChild(overlay); + requestAnimationFrame(() => overlay.classList.add('is-visible')); + + const finish = href => { + const delay = reduceMotion ? 80 : 1250; + window.setTimeout(() => { + location.href = href; + }, delay); + }; + + const startCardFlight = posts => { + if (reduceMotion) return; + + const flight = overlay.querySelector('.random-post-flight'); + if (!flight) return; + + const candidates = posts + .filter(post => post && typeof post.href === 'string' && post.href.startsWith('/posts/')) + .map(post => ({ + title: typeof post.title === 'string' && post.title.trim() ? post.title.trim() : '\u672a\u547d\u540d\u6587\u7ae0', + meta: typeof post.category === 'string' && post.category.trim() ? post.category.trim() : '\u968f\u673a\u6587\u7ae0', + })); + + const pool = candidates.length ? candidates : [{ title: '\u968f\u673a\u6587\u7ae0', meta: '\u6b63\u5728\u62bd\u53d6' }]; + const count = Math.min(9, Math.max(5, pool.length)); + + for (let index = 0; index < count; index += 1) { + const post = pool[Math.floor(Math.random() * pool.length)]; + const card = document.createElement('div'); + card.className = 'random-post-flying-card'; + card.style.setProperty('--flight-delay', `${index * 0.11}s`); + card.style.setProperty('--flight-y', `${-42 + Math.random() * 84}px`); + card.style.setProperty('--flight-rotate', `${-8 + Math.random() * 16}deg`); + const meta = document.createElement('small'); + const title = document.createElement('strong'); + meta.textContent = post.meta; + title.textContent = post.title; + card.append(meta, title); + flight.appendChild(card); + } + }; + + const reset = () => { + source?.classList.remove('is-randomizing'); + source?.removeAttribute('aria-busy'); + overlay.classList.remove('is-visible'); + window.setTimeout(() => overlay.remove(), reduceMotion ? 0 : 180); + }; + fetch('/api/posts.json') .then(res => { if (!res.ok) throw new Error(`Failed to load posts: ${res.status}`); @@ -11,12 +79,15 @@ function randomPost() { if (postLinks.length === 0) { console.warn('No posts available for random navigation.'); + reset(); return; } - location.href = postLinks[Math.floor(Math.random() * postLinks.length)]; + startCardFlight(posts); + finish(postLinks[Math.floor(Math.random() * postLinks.length)]); }) .catch(error => { console.error('Random post navigation failed:', error); + reset(); }); } diff --git a/src/components/CustomContextMenu.astro b/src/components/CustomContextMenu.astro index a4bff7b..a30f9f2 100644 --- a/src/components/CustomContextMenu.astro +++ b/src/components/CustomContextMenu.astro @@ -57,6 +57,10 @@ 标签 +