feat: story-journey immersive layer (progress, scene focus, floating orbs)
All checks were successful
Deploy / deploy (push) Successful in 2s

This commit is contained in:
Chen Gu
2026-04-24 09:31:21 +08:00
parent dbd2159429
commit 82a2d231c3
4 changed files with 178 additions and 3 deletions

View File

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

View File

@@ -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();
});
})();

View File

@@ -10,7 +10,7 @@
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
<link rel="alternate" type="application/rss+xml" href='{{ "feed.xml" | relURL }}'>
</head>
<body>
<body class="kind-{{ .Kind }}{{ with .Section }} section-{{ . }}{{ end }}">
<canvas id="starfield"></canvas>
<div id="app">
<nav class="nav">

View File

@@ -1,5 +1,9 @@
{{ define "main" }}
<article class="dream-article">
<article class="dream-article story-journey" data-story="journey">
<div class="story-orb orb-1"></div>
<div class="story-orb orb-2"></div>
<div class="story-orb orb-3"></div>
<div class="article-hero reveal">
<div class="article-glyph"></div>
<h1 class="article-title">{{ .Title }}</h1>
@@ -11,7 +15,11 @@
</div>
</div>
<div class="article-body">
<div class="story-progress">
<div class="story-progress-bar" id="storyProgress"></div>
</div>
<div class="article-body" id="storyBody">
{{ .Content }}
</div>