feat: story-journey immersive layer (progress, scene focus, floating orbs)
All checks were successful
Deploy / deploy (push) Successful in 2s
All checks were successful
Deploy / deploy (push) Successful in 2s
This commit is contained in:
@@ -253,6 +253,121 @@ main {
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
/* ═══ 故事沉浸层(文章页) ═══ */
|
||||
.story-journey {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.story-orb {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
border-radius: 50%;
|
||||
filter: blur(36px);
|
||||
opacity: 0.35;
|
||||
mix-blend-mode: screen;
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
.orb-1 {
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
top: 14%;
|
||||
left: -80px;
|
||||
background: radial-gradient(circle, rgba(123,104,238,0.42), transparent 70%);
|
||||
}
|
||||
|
||||
.orb-2 {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
top: 48%;
|
||||
right: -60px;
|
||||
background: radial-gradient(circle, rgba(34,211,238,0.34), transparent 72%);
|
||||
}
|
||||
|
||||
.orb-3 {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
bottom: 6%;
|
||||
left: 24%;
|
||||
background: radial-gradient(circle, rgba(74,144,217,0.30), transparent 72%);
|
||||
}
|
||||
|
||||
.story-progress {
|
||||
position: sticky;
|
||||
top: 62px;
|
||||
height: 4px;
|
||||
background: rgba(123,104,238,0.18);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
margin: -1rem 0 2rem;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.story-progress-bar {
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
background: linear-gradient(90deg, var(--glow-purple), var(--glow-cyan));
|
||||
box-shadow: 0 0 18px rgba(123,104,238,0.6);
|
||||
transition: width 0.08s linear;
|
||||
}
|
||||
|
||||
/* 场景段落:像旅程节点 */
|
||||
.article-body > p {
|
||||
position: relative;
|
||||
padding-left: 1.1rem;
|
||||
}
|
||||
|
||||
.article-body > p::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -0.15rem;
|
||||
top: 0.85rem;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--glow-purple);
|
||||
box-shadow: 0 0 12px rgba(123,104,238,0.8);
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.article-body > p.active-scene::before {
|
||||
background: var(--glow-cyan);
|
||||
box-shadow: 0 0 16px rgba(34,211,238,0.9);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.article-body > p.active-scene {
|
||||
text-shadow: 0 0 18px rgba(123,104,238,0.22);
|
||||
}
|
||||
|
||||
/* 手机端沉浸优化 */
|
||||
@media (max-width: 640px) {
|
||||
.story-progress {
|
||||
top: 54px;
|
||||
margin: -0.5rem 0 1.2rem;
|
||||
}
|
||||
.story-orb {
|
||||
filter: blur(26px);
|
||||
opacity: 0.28;
|
||||
}
|
||||
.orb-1 { width: 180px; height: 180px; left: -60px; }
|
||||
.orb-2 { width: 150px; height: 150px; right: -45px; }
|
||||
.orb-3 { width: 190px; height: 190px; left: 20%; }
|
||||
}
|
||||
|
||||
/* 降级策略:用户偏好减少动画 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.story-orb,
|
||||
.story-progress-bar,
|
||||
.reveal,
|
||||
.js-ready .article-body p {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══ 文章页 ═══ */
|
||||
.dream-article {
|
||||
padding-top: 2rem;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
// ═══ 星空粒子系统 ═══
|
||||
const canvas = document.getElementById('starfield');
|
||||
if (!canvas) return;
|
||||
const ctx = canvas.getContext('2d');
|
||||
let width, height, stars, nebulas;
|
||||
let animationId;
|
||||
@@ -133,6 +134,55 @@
|
||||
});
|
||||
}
|
||||
|
||||
// ═══ 场景滚动进度 + 旅程节点高亮 ═══
|
||||
function setupStoryJourney() {
|
||||
const body = document.getElementById('storyBody');
|
||||
const progress = document.getElementById('storyProgress');
|
||||
if (!body || !progress) return;
|
||||
|
||||
const scenes = Array.from(body.querySelectorAll(':scope > p'));
|
||||
if (!scenes.length) return;
|
||||
|
||||
const onScroll = () => {
|
||||
const rect = body.getBoundingClientRect();
|
||||
const total = Math.max(body.scrollHeight - window.innerHeight, 1);
|
||||
const scrolled = Math.min(Math.max(-rect.top, 0), total);
|
||||
const ratio = scrolled / total;
|
||||
progress.style.width = `${(ratio * 100).toFixed(2)}%`;
|
||||
|
||||
let activeIndex = 0;
|
||||
let minDistance = Infinity;
|
||||
scenes.forEach((p, idx) => {
|
||||
const r = p.getBoundingClientRect();
|
||||
const d = Math.abs(r.top - window.innerHeight * 0.35);
|
||||
if (d < minDistance) {
|
||||
minDistance = d;
|
||||
activeIndex = idx;
|
||||
}
|
||||
});
|
||||
scenes.forEach((p, idx) => p.classList.toggle('active-scene', idx === activeIndex));
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
onScroll();
|
||||
}
|
||||
|
||||
// ═══ 漂浮光团轻微视差 ═══
|
||||
function setupOrbsParallax() {
|
||||
const orbs = document.querySelectorAll('.story-orb');
|
||||
if (!orbs.length) return;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const y = window.scrollY;
|
||||
orbs.forEach((orb, i) => {
|
||||
const factor = (i + 1) * 0.04;
|
||||
const xShift = Math.sin(y * 0.002 + i) * 10;
|
||||
const yShift = y * factor;
|
||||
orb.style.transform = `translate(${xShift}px, ${yShift}px)`;
|
||||
});
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ═══ 初始化 ═══
|
||||
window.addEventListener('resize', () => {
|
||||
resize();
|
||||
@@ -146,6 +196,8 @@
|
||||
setupReveal();
|
||||
setupParallax();
|
||||
setupGlow();
|
||||
setupStoryJourney();
|
||||
setupOrbsParallax();
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user