@anglefeint/astro-theme 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/i18n/messages.ts +24 -0
- package/src/layouts/BlogPost.astro +24 -6
- package/src/scripts/blogpost/hero-canvas.js +93 -9
- package/src/scripts/blogpost/read-progress.js +23 -6
- package/src/scripts/blogpost/red-queen-tv.js +63 -12
- package/src/scripts/blogpost-effects.js +41 -4
- package/src/styles/ai/base.css +15 -3
- package/src/styles/blog-post.css +22 -13
package/package.json
CHANGED
package/src/i18n/messages.ts
CHANGED
|
@@ -36,6 +36,10 @@ export type Messages = {
|
|
|
36
36
|
backToBlog: string;
|
|
37
37
|
related: string;
|
|
38
38
|
regenerate: string;
|
|
39
|
+
toastP10: string;
|
|
40
|
+
toastP30: string;
|
|
41
|
+
toastP60: string;
|
|
42
|
+
toastDone: string;
|
|
39
43
|
};
|
|
40
44
|
};
|
|
41
45
|
|
|
@@ -71,6 +75,10 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
71
75
|
backToBlog: 'Back to blog',
|
|
72
76
|
related: 'Related',
|
|
73
77
|
regenerate: 'Regenerate',
|
|
78
|
+
toastP10: 'context parsed 10%',
|
|
79
|
+
toastP30: 'context parsed 30%',
|
|
80
|
+
toastP60: 'inference stable 60%',
|
|
81
|
+
toastDone: 'output finalized',
|
|
74
82
|
},
|
|
75
83
|
},
|
|
76
84
|
ja: {
|
|
@@ -104,6 +112,10 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
104
112
|
backToBlog: 'ブログへ戻る',
|
|
105
113
|
related: '関連記事',
|
|
106
114
|
regenerate: '再生成',
|
|
115
|
+
toastP10: '文脈解析 10%',
|
|
116
|
+
toastP30: '文脈解析 30%',
|
|
117
|
+
toastP60: '推論安定 60%',
|
|
118
|
+
toastDone: '出力確定',
|
|
107
119
|
},
|
|
108
120
|
},
|
|
109
121
|
ko: {
|
|
@@ -137,6 +149,10 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
137
149
|
backToBlog: '블로그로 돌아가기',
|
|
138
150
|
related: '관련 글',
|
|
139
151
|
regenerate: '재생성',
|
|
152
|
+
toastP10: '컨텍스트 파싱 10%',
|
|
153
|
+
toastP30: '컨텍스트 파싱 30%',
|
|
154
|
+
toastP60: '추론 안정화 60%',
|
|
155
|
+
toastDone: '출력 완료',
|
|
140
156
|
},
|
|
141
157
|
},
|
|
142
158
|
es: {
|
|
@@ -170,6 +186,10 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
170
186
|
backToBlog: 'Volver al blog',
|
|
171
187
|
related: 'Relacionados',
|
|
172
188
|
regenerate: 'Regenerar',
|
|
189
|
+
toastP10: 'contexto analizado 10%',
|
|
190
|
+
toastP30: 'contexto analizado 30%',
|
|
191
|
+
toastP60: 'inferencia estable 60%',
|
|
192
|
+
toastDone: 'salida finalizada',
|
|
173
193
|
},
|
|
174
194
|
},
|
|
175
195
|
zh: {
|
|
@@ -203,6 +223,10 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
203
223
|
backToBlog: '返回博客',
|
|
204
224
|
related: '相关文章',
|
|
205
225
|
regenerate: '重新生成',
|
|
226
|
+
toastP10: '语境解析 10%',
|
|
227
|
+
toastP30: '语境解析 30%',
|
|
228
|
+
toastP60: '推理稳定 60%',
|
|
229
|
+
toastDone: '输出完成',
|
|
206
230
|
},
|
|
207
231
|
},
|
|
208
232
|
};
|
|
@@ -90,7 +90,15 @@ const enableRedQueen = THEME.EFFECTS.ENABLE_RED_QUEEN;
|
|
|
90
90
|
)}
|
|
91
91
|
<div class="ai-read-progress" aria-hidden="true"></div>
|
|
92
92
|
<button type="button" class="ai-back-to-top" aria-label="Back to top" title="Back to top">↑</button>
|
|
93
|
-
|
|
93
|
+
<div
|
|
94
|
+
class="ai-stage-toast"
|
|
95
|
+
aria-live="polite"
|
|
96
|
+
aria-atomic="true"
|
|
97
|
+
data-toast-p10={messages.blog.toastP10}
|
|
98
|
+
data-toast-p30={messages.blog.toastP30}
|
|
99
|
+
data-toast-p60={messages.blog.toastP60}
|
|
100
|
+
data-toast-done={messages.blog.toastDone}
|
|
101
|
+
></div>
|
|
94
102
|
<div class="ai-mouse-glow" aria-hidden="true"></div>
|
|
95
103
|
<div class="ai-depth-blur" aria-hidden="true"></div>
|
|
96
104
|
<div class="ai-thinking-dots" aria-hidden="true"><span></span><span></span><span></span></div>
|
|
@@ -100,12 +108,22 @@ const enableRedQueen = THEME.EFFECTS.ENABLE_RED_QUEEN;
|
|
|
100
108
|
{heroImage && (
|
|
101
109
|
<div class="hero-shell">
|
|
102
110
|
<div class="hero-pane">
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
<div class="hero-image">
|
|
112
|
+
<div class="hero-stack">
|
|
113
|
+
<div class="hero-base-wrap">
|
|
114
|
+
<Image
|
|
115
|
+
class="hero-base-image"
|
|
116
|
+
src={heroImage}
|
|
117
|
+
alt={title}
|
|
118
|
+
loading="eager"
|
|
119
|
+
decoding="async"
|
|
120
|
+
fetchpriority="high"
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="hero-canvas-wrap" aria-hidden="true">
|
|
124
|
+
<canvas class="hero-canvas" data-hero-src={heroImage.src}></canvas>
|
|
125
|
+
</div>
|
|
107
126
|
</div>
|
|
108
|
-
</div>
|
|
109
127
|
<div class="hero-frame" aria-hidden="true">
|
|
110
128
|
<span>neural monitor</span>
|
|
111
129
|
<span class="hero-frame-dot"></span>
|
|
@@ -15,6 +15,12 @@ export function initHeroCanvas(prefersReducedMotion) {
|
|
|
15
15
|
var heroStart = 0;
|
|
16
16
|
var heroRaf = 0;
|
|
17
17
|
var resizeTimer = 0;
|
|
18
|
+
var prepareTimer = 0;
|
|
19
|
+
var prepareIdleHandle = 0;
|
|
20
|
+
var isInViewport = true;
|
|
21
|
+
var heroObserver = null;
|
|
22
|
+
var heroEffectsPrepared = false;
|
|
23
|
+
var heroEffectsPreparing = false;
|
|
18
24
|
var baseCanvas = document.createElement('canvas');
|
|
19
25
|
var baseCtx = baseCanvas.getContext('2d');
|
|
20
26
|
var pixelCanvas = document.createElement('canvas');
|
|
@@ -117,6 +123,10 @@ export function initHeroCanvas(prefersReducedMotion) {
|
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
function heroRender(t) {
|
|
126
|
+
if (prefersReducedMotion || document.hidden || !isInViewport) {
|
|
127
|
+
heroRaf = 0;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
120
130
|
if (!heroStart) heroStart = t;
|
|
121
131
|
var elapsed = (t - heroStart) * 0.001;
|
|
122
132
|
frameCount++;
|
|
@@ -237,20 +247,78 @@ export function initHeroCanvas(prefersReducedMotion) {
|
|
|
237
247
|
}
|
|
238
248
|
}
|
|
239
249
|
|
|
250
|
+
if (!prefersReducedMotion && !document.hidden && isInViewport) {
|
|
251
|
+
heroRaf = requestAnimationFrame(heroRender);
|
|
252
|
+
} else {
|
|
253
|
+
heroRaf = 0;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function startHeroLoop() {
|
|
258
|
+
if (prefersReducedMotion || document.hidden || !isInViewport || !canvas.img || !heroEffectsPrepared || heroRaf) return;
|
|
240
259
|
heroRaf = requestAnimationFrame(heroRender);
|
|
241
260
|
}
|
|
242
261
|
|
|
262
|
+
function stopHeroLoop() {
|
|
263
|
+
if (!heroRaf) return;
|
|
264
|
+
cancelAnimationFrame(heroRaf);
|
|
265
|
+
heroRaf = 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function drawStaticFrame(img) {
|
|
269
|
+
var w = canvas.width;
|
|
270
|
+
var h = canvas.height;
|
|
271
|
+
baseCanvas.width = w;
|
|
272
|
+
baseCanvas.height = h;
|
|
273
|
+
drawBase(baseCtx, img, w, h);
|
|
274
|
+
ctx.clearRect(0, 0, w, h);
|
|
275
|
+
ctx.drawImage(baseCanvas, 0, 0);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function scheduleHeroEffectsPrepare() {
|
|
279
|
+
if (prefersReducedMotion || !canvas.img || heroEffectsPrepared || heroEffectsPreparing) return;
|
|
280
|
+
heroEffectsPreparing = true;
|
|
281
|
+
|
|
282
|
+
function runPrepare() {
|
|
283
|
+
prepareTimer = 0;
|
|
284
|
+
prepareIdleHandle = 0;
|
|
285
|
+
if (!canvas.img) {
|
|
286
|
+
heroEffectsPreparing = false;
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
buildEdge(canvas.img);
|
|
290
|
+
heroEffectsPrepared = true;
|
|
291
|
+
heroEffectsPreparing = false;
|
|
292
|
+
startHeroLoop();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
296
|
+
prepareIdleHandle = window.requestIdleCallback(runPrepare, { timeout: 1200 });
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function afterLoad() {
|
|
301
|
+
prepareTimer = setTimeout(runPrepare, 180);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (document.readyState === 'complete') afterLoad();
|
|
305
|
+
else window.addEventListener('load', afterLoad, { once: true });
|
|
306
|
+
}
|
|
307
|
+
|
|
243
308
|
var img = new Image();
|
|
244
309
|
img.onload = function() {
|
|
245
310
|
canvas.img = img;
|
|
246
311
|
sizeCanvas();
|
|
247
|
-
|
|
312
|
+
drawStaticFrame(img);
|
|
248
313
|
wrap.classList.add('ready');
|
|
249
314
|
if (prefersReducedMotion) {
|
|
250
|
-
ctx.drawImage(baseCanvas, 0, 0);
|
|
251
315
|
return;
|
|
252
316
|
}
|
|
253
|
-
|
|
317
|
+
// Keep first paint static for LCP, then prepare heavy effects when idle.
|
|
318
|
+
scheduleHeroEffectsPrepare();
|
|
319
|
+
};
|
|
320
|
+
img.onerror = function() {
|
|
321
|
+
wrap.classList.add('ready');
|
|
254
322
|
};
|
|
255
323
|
img.src = new URL(src, window.location.href).href;
|
|
256
324
|
|
|
@@ -260,23 +328,39 @@ export function initHeroCanvas(prefersReducedMotion) {
|
|
|
260
328
|
resizeTimer = setTimeout(function() {
|
|
261
329
|
resizeTimer = 0;
|
|
262
330
|
sizeCanvas();
|
|
263
|
-
|
|
331
|
+
drawStaticFrame(canvas.img);
|
|
332
|
+
if (heroEffectsPrepared) buildEdge(canvas.img);
|
|
264
333
|
}, 180);
|
|
265
334
|
}, { passive: true });
|
|
266
335
|
|
|
267
336
|
function onHeroVisibilityChange() {
|
|
268
337
|
if (prefersReducedMotion) return;
|
|
269
338
|
if (document.hidden) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
heroRaf = requestAnimationFrame(heroRender);
|
|
339
|
+
stopHeroLoop();
|
|
340
|
+
} else {
|
|
341
|
+
startHeroLoop();
|
|
274
342
|
}
|
|
275
343
|
}
|
|
276
344
|
|
|
345
|
+
if (!prefersReducedMotion && typeof IntersectionObserver !== 'undefined') {
|
|
346
|
+
heroObserver = new IntersectionObserver(function(entries) {
|
|
347
|
+
var entry = entries[0];
|
|
348
|
+
if (!entry) return;
|
|
349
|
+
isInViewport = entry.isIntersecting;
|
|
350
|
+
if (isInViewport) startHeroLoop();
|
|
351
|
+
else stopHeroLoop();
|
|
352
|
+
}, { threshold: 0.02 });
|
|
353
|
+
heroObserver.observe(shell);
|
|
354
|
+
}
|
|
355
|
+
|
|
277
356
|
document.addEventListener('visibilitychange', onHeroVisibilityChange);
|
|
278
357
|
window.addEventListener('beforeunload', function() {
|
|
279
358
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
280
|
-
|
|
359
|
+
stopHeroLoop();
|
|
360
|
+
if (prepareTimer) clearTimeout(prepareTimer);
|
|
361
|
+
if (prepareIdleHandle && typeof window.cancelIdleCallback === 'function') {
|
|
362
|
+
window.cancelIdleCallback(prepareIdleHandle);
|
|
363
|
+
}
|
|
364
|
+
if (heroObserver) heroObserver.disconnect();
|
|
281
365
|
}, { once: true });
|
|
282
366
|
}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
export function initReadProgressAndBackToTop(prefersReducedMotion) {
|
|
2
2
|
var progress = document.querySelector('.ai-read-progress');
|
|
3
3
|
var toast = document.querySelector('.ai-stage-toast');
|
|
4
|
-
var stageSeen = { p30: false, p60: false,
|
|
4
|
+
var stageSeen = { p10: false, p30: false, p60: false, done: false };
|
|
5
5
|
var toastTimer = 0;
|
|
6
6
|
var hasScrolled = false;
|
|
7
|
+
var toastText = {
|
|
8
|
+
p10: 'context parsed 10%',
|
|
9
|
+
p30: 'context parsed 30%',
|
|
10
|
+
p60: 'inference stable 60%',
|
|
11
|
+
done: 'output finalized',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
if (toast && toast.dataset) {
|
|
15
|
+
toastText.p10 = toast.dataset.toastP10 || toastText.p10;
|
|
16
|
+
toastText.p30 = toast.dataset.toastP30 || toastText.p30;
|
|
17
|
+
toastText.p60 = toast.dataset.toastP60 || toastText.p60;
|
|
18
|
+
toastText.done = toast.dataset.toastDone || toastText.done;
|
|
19
|
+
}
|
|
7
20
|
|
|
8
21
|
function showStageToast(msg) {
|
|
9
22
|
if (!toast) return;
|
|
@@ -25,17 +38,21 @@ export function initReadProgressAndBackToTop(prefersReducedMotion) {
|
|
|
25
38
|
if (btn) btn.classList.toggle('visible', scrollTop > 400);
|
|
26
39
|
if (!hasScrolled && scrollTop > 6) hasScrolled = true;
|
|
27
40
|
if (!hasScrolled) return;
|
|
41
|
+
if (!stageSeen.p10 && p >= 0.1) {
|
|
42
|
+
stageSeen.p10 = true;
|
|
43
|
+
showStageToast(toastText.p10);
|
|
44
|
+
}
|
|
28
45
|
if (!stageSeen.p30 && p >= 0.3) {
|
|
29
46
|
stageSeen.p30 = true;
|
|
30
|
-
showStageToast(
|
|
47
|
+
showStageToast(toastText.p30);
|
|
31
48
|
}
|
|
32
49
|
if (!stageSeen.p60 && p >= 0.6) {
|
|
33
50
|
stageSeen.p60 = true;
|
|
34
|
-
showStageToast(
|
|
51
|
+
showStageToast(toastText.p60);
|
|
35
52
|
}
|
|
36
|
-
if (!stageSeen.
|
|
37
|
-
stageSeen.
|
|
38
|
-
showStageToast(
|
|
53
|
+
if (!stageSeen.done && p >= 0.9) {
|
|
54
|
+
stageSeen.done = true;
|
|
55
|
+
showStageToast(toastText.done);
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
58
|
|
|
@@ -8,7 +8,7 @@ export function initRedQueenTv(prefersReducedMotion) {
|
|
|
8
8
|
var source2 = stage.getAttribute('data-rq-src2') || '';
|
|
9
9
|
if (!source) return;
|
|
10
10
|
|
|
11
|
-
var OPEN_DELAY_MS =
|
|
11
|
+
var OPEN_DELAY_MS = 2000;
|
|
12
12
|
var COLLAPSE_DELAY_MS = 260;
|
|
13
13
|
var FRAME_MS = 33;
|
|
14
14
|
var FALLBACK_STEP_MS = 650;
|
|
@@ -24,6 +24,10 @@ export function initRedQueenTv(prefersReducedMotion) {
|
|
|
24
24
|
var revealTimer = 0;
|
|
25
25
|
var preloadTimeoutTimer = 0;
|
|
26
26
|
var preloadRetryTimers = new Set();
|
|
27
|
+
var autoDelayTimer = 0;
|
|
28
|
+
var autoIdleHandle = 0;
|
|
29
|
+
var autoLoadHandler = null;
|
|
30
|
+
var autoStarted = false;
|
|
27
31
|
|
|
28
32
|
var start = 0;
|
|
29
33
|
var width = 320;
|
|
@@ -105,6 +109,21 @@ export function initRedQueenTv(prefersReducedMotion) {
|
|
|
105
109
|
preloadRetryTimers.clear();
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
function clearAutoStartTimers() {
|
|
113
|
+
if (autoDelayTimer) {
|
|
114
|
+
clearTimeout(autoDelayTimer);
|
|
115
|
+
autoDelayTimer = 0;
|
|
116
|
+
}
|
|
117
|
+
if (autoIdleHandle && typeof window.cancelIdleCallback === 'function') {
|
|
118
|
+
window.cancelIdleCallback(autoIdleHandle);
|
|
119
|
+
autoIdleHandle = 0;
|
|
120
|
+
}
|
|
121
|
+
if (autoLoadHandler) {
|
|
122
|
+
window.removeEventListener('load', autoLoadHandler);
|
|
123
|
+
autoLoadHandler = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
108
127
|
function scheduleSequence(fn, delay) {
|
|
109
128
|
clearSequenceTimer();
|
|
110
129
|
sequenceTimer = setTimeout(function runTask() {
|
|
@@ -283,6 +302,7 @@ export function initRedQueenTv(prefersReducedMotion) {
|
|
|
283
302
|
forceStaticUntil = 0;
|
|
284
303
|
clearSequenceTimer();
|
|
285
304
|
clearPreloadTimers();
|
|
305
|
+
clearAutoStartTimers();
|
|
286
306
|
if (revealTimer) {
|
|
287
307
|
clearTimeout(revealTimer);
|
|
288
308
|
revealTimer = 0;
|
|
@@ -535,22 +555,53 @@ export function initRedQueenTv(prefersReducedMotion) {
|
|
|
535
555
|
}
|
|
536
556
|
|
|
537
557
|
function queueAutoPlay() {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
558
|
+
var readyByDelay = false;
|
|
559
|
+
var readyByLoad = document.readyState === 'complete';
|
|
560
|
+
var readyByIdle = false;
|
|
561
|
+
|
|
562
|
+
function tryAutoStart() {
|
|
563
|
+
if (autoStarted || !readyByDelay || !readyByLoad || !readyByIdle) return;
|
|
564
|
+
if (document.hidden) {
|
|
565
|
+
pendingAutoPlay = true;
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
autoStarted = true;
|
|
569
|
+
clearAutoStartTimers();
|
|
570
|
+
startPlayback();
|
|
547
571
|
}
|
|
548
572
|
|
|
549
|
-
|
|
550
|
-
|
|
573
|
+
autoDelayTimer = setTimeout(function() {
|
|
574
|
+
autoDelayTimer = 0;
|
|
575
|
+
readyByDelay = true;
|
|
576
|
+
tryAutoStart();
|
|
577
|
+
}, OPEN_DELAY_MS);
|
|
578
|
+
|
|
579
|
+
if (!readyByLoad) {
|
|
580
|
+
autoLoadHandler = function() {
|
|
581
|
+
readyByLoad = true;
|
|
582
|
+
autoLoadHandler = null;
|
|
583
|
+
tryAutoStart();
|
|
584
|
+
};
|
|
585
|
+
window.addEventListener('load', autoLoadHandler, { once: true });
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (typeof window.requestIdleCallback === 'function') {
|
|
589
|
+
autoIdleHandle = window.requestIdleCallback(function() {
|
|
590
|
+
autoIdleHandle = 0;
|
|
591
|
+
readyByIdle = true;
|
|
592
|
+
tryAutoStart();
|
|
593
|
+
}, { timeout: 1500 });
|
|
594
|
+
} else {
|
|
595
|
+
setTimeout(function() {
|
|
596
|
+
readyByIdle = true;
|
|
597
|
+
tryAutoStart();
|
|
598
|
+
}, 600);
|
|
599
|
+
}
|
|
551
600
|
}
|
|
552
601
|
|
|
553
602
|
toggle.addEventListener('click', function() {
|
|
603
|
+
autoStarted = true;
|
|
604
|
+
clearAutoStartTimers();
|
|
554
605
|
startPlayback();
|
|
555
606
|
});
|
|
556
607
|
|
|
@@ -14,11 +14,48 @@ function prefersReducedMotionEnabled() {
|
|
|
14
14
|
|
|
15
15
|
export function initBlogpostEffects() {
|
|
16
16
|
var prefersReducedMotion = prefersReducedMotionEnabled();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
var networkStarted = false;
|
|
18
|
+
var redQueenStarted = false;
|
|
19
|
+
|
|
20
|
+
function startNetworkCanvas() {
|
|
21
|
+
if (networkStarted) return;
|
|
22
|
+
networkStarted = true;
|
|
23
|
+
initNetworkCanvas(prefersReducedMotion);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function startRedQueenTv() {
|
|
27
|
+
if (redQueenStarted) return;
|
|
28
|
+
if (!document.querySelector('.rq-tv-stage')) return;
|
|
29
|
+
redQueenStarted = true;
|
|
21
30
|
initRedQueenTv(prefersReducedMotion);
|
|
22
31
|
}
|
|
32
|
+
|
|
33
|
+
function scheduleDeferredStarts() {
|
|
34
|
+
function fallbackSchedule() {
|
|
35
|
+
function runAfterLoad() {
|
|
36
|
+
setTimeout(startNetworkCanvas, 120);
|
|
37
|
+
setTimeout(startRedQueenTv, 460);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (document.readyState === 'complete') runAfterLoad();
|
|
41
|
+
else window.addEventListener('load', runAfterLoad, { once: true });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof window.requestIdleCallback !== 'function') {
|
|
45
|
+
fallbackSchedule();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
window.requestIdleCallback(function() {
|
|
50
|
+
startNetworkCanvas();
|
|
51
|
+
window.requestIdleCallback(function() {
|
|
52
|
+
startRedQueenTv();
|
|
53
|
+
}, { timeout: 2200 });
|
|
54
|
+
}, { timeout: 1200 });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
initReadProgressAndBackToTop(prefersReducedMotion);
|
|
58
|
+
initHeroCanvas(prefersReducedMotion);
|
|
23
59
|
initPostInteractions(prefersReducedMotion);
|
|
60
|
+
scheduleDeferredStarts();
|
|
24
61
|
}
|
package/src/styles/ai/base.css
CHANGED
|
@@ -89,19 +89,31 @@ body.ai-page::-webkit-scrollbar-thumb {
|
|
|
89
89
|
#13070f 100%
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
|
+
.ai-glow::after {
|
|
93
|
+
content: "";
|
|
94
|
+
position: absolute;
|
|
95
|
+
inset: 0;
|
|
96
|
+
pointer-events: none;
|
|
97
|
+
background:
|
|
98
|
+
radial-gradient(circle at 30% 28%, rgba(164, 220, 255, 0.1), rgba(164, 220, 255, 0) 46%),
|
|
99
|
+
radial-gradient(circle at 72% 64%, rgba(255, 112, 140, 0.08), rgba(255, 112, 140, 0) 52%);
|
|
100
|
+
opacity: 0.38;
|
|
101
|
+
transform: translateZ(0);
|
|
102
|
+
will-change: opacity;
|
|
103
|
+
}
|
|
92
104
|
.ai-glow-shift {
|
|
93
105
|
animation: ai-glow-shift 25s ease-in-out infinite;
|
|
94
106
|
}
|
|
95
107
|
@keyframes ai-glow-shift {
|
|
96
108
|
0%,
|
|
97
109
|
100% {
|
|
98
|
-
|
|
110
|
+
opacity: 0.32;
|
|
99
111
|
}
|
|
100
112
|
33% {
|
|
101
|
-
|
|
113
|
+
opacity: 0.52;
|
|
102
114
|
}
|
|
103
115
|
66% {
|
|
104
|
-
|
|
116
|
+
opacity: 0.4;
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
/* 柔和光晕:中心径向青蓝光 */
|
package/src/styles/blog-post.css
CHANGED
|
@@ -27,16 +27,27 @@
|
|
|
27
27
|
0 24px 48px rgba(8, 18, 30, 0.68),
|
|
28
28
|
0 0 34px rgba(8, 18, 30, 0.64);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
.hero-stack {
|
|
31
|
+
position: relative;
|
|
32
|
+
aspect-ratio: 1020 / 510;
|
|
33
|
+
background: #0a1218;
|
|
34
|
+
}
|
|
35
|
+
.hero-base-wrap {
|
|
36
|
+
position: absolute;
|
|
37
|
+
inset: 0;
|
|
38
|
+
z-index: 0;
|
|
39
|
+
}
|
|
40
|
+
.hero-base-image {
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
object-fit: cover;
|
|
44
|
+
display: block;
|
|
45
|
+
}
|
|
46
|
+
.hero-canvas-wrap {
|
|
47
|
+
position: absolute;
|
|
48
|
+
inset: 0;
|
|
49
|
+
z-index: 1;
|
|
50
|
+
opacity: 0;
|
|
40
51
|
transition: opacity 260ms ease;
|
|
41
52
|
}
|
|
42
53
|
.hero-canvas-wrap.ready { opacity: 1; }
|
|
@@ -154,9 +165,7 @@
|
|
|
154
165
|
will-change: transform, opacity;
|
|
155
166
|
transition:
|
|
156
167
|
transform 320ms ease,
|
|
157
|
-
opacity 220ms ease
|
|
158
|
-
box-shadow 320ms ease,
|
|
159
|
-
background 320ms ease;
|
|
168
|
+
opacity 220ms ease;
|
|
160
169
|
}
|
|
161
170
|
.rq-tv::before {
|
|
162
171
|
content: "";
|