@anglefeint/astro-theme 0.1.22 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anglefeint/astro-theme",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "type": "module",
5
5
  "description": "Anglefeint core theme package for Astro",
6
6
  "keywords": [
@@ -108,12 +108,22 @@ const enableRedQueen = THEME.EFFECTS.ENABLE_RED_QUEEN;
108
108
  {heroImage && (
109
109
  <div class="hero-shell">
110
110
  <div class="hero-pane">
111
- <div class="hero-image">
112
- <div class="hero-stack">
113
- <div class="hero-canvas-wrap" aria-hidden="true">
114
- <canvas class="hero-canvas" data-hero-src={heroImage.src}></canvas>
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>
115
126
  </div>
116
- </div>
117
127
  <div class="hero-frame" aria-hidden="true">
118
128
  <span>neural monitor</span>
119
129
  <span class="hero-frame-dot"></span>
@@ -15,8 +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;
18
20
  var isInViewport = true;
19
21
  var heroObserver = null;
22
+ var heroEffectsPrepared = false;
23
+ var heroEffectsPreparing = false;
20
24
  var baseCanvas = document.createElement('canvas');
21
25
  var baseCtx = baseCanvas.getContext('2d');
22
26
  var pixelCanvas = document.createElement('canvas');
@@ -251,7 +255,7 @@ export function initHeroCanvas(prefersReducedMotion) {
251
255
  }
252
256
 
253
257
  function startHeroLoop() {
254
- if (prefersReducedMotion || document.hidden || !isInViewport || !canvas.img || heroRaf) return;
258
+ if (prefersReducedMotion || document.hidden || !isInViewport || !canvas.img || !heroEffectsPrepared || heroRaf) return;
255
259
  heroRaf = requestAnimationFrame(heroRender);
256
260
  }
257
261
 
@@ -261,17 +265,60 @@ export function initHeroCanvas(prefersReducedMotion) {
261
265
  heroRaf = 0;
262
266
  }
263
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
+
264
308
  var img = new Image();
265
309
  img.onload = function() {
266
310
  canvas.img = img;
267
311
  sizeCanvas();
268
- buildEdge(img);
312
+ drawStaticFrame(img);
269
313
  wrap.classList.add('ready');
270
314
  if (prefersReducedMotion) {
271
- ctx.drawImage(baseCanvas, 0, 0);
272
315
  return;
273
316
  }
274
- startHeroLoop();
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');
275
322
  };
276
323
  img.src = new URL(src, window.location.href).href;
277
324
 
@@ -281,7 +328,8 @@ export function initHeroCanvas(prefersReducedMotion) {
281
328
  resizeTimer = setTimeout(function() {
282
329
  resizeTimer = 0;
283
330
  sizeCanvas();
284
- buildEdge(canvas.img);
331
+ drawStaticFrame(canvas.img);
332
+ if (heroEffectsPrepared) buildEdge(canvas.img);
285
333
  }, 180);
286
334
  }, { passive: true });
287
335
 
@@ -309,6 +357,10 @@ export function initHeroCanvas(prefersReducedMotion) {
309
357
  window.addEventListener('beforeunload', function() {
310
358
  if (resizeTimer) clearTimeout(resizeTimer);
311
359
  stopHeroLoop();
360
+ if (prepareTimer) clearTimeout(prepareTimer);
361
+ if (prepareIdleHandle && typeof window.cancelIdleCallback === 'function') {
362
+ window.cancelIdleCallback(prepareIdleHandle);
363
+ }
312
364
  if (heroObserver) heroObserver.disconnect();
313
365
  }, { once: true });
314
366
  }
@@ -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 = 500;
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
- function run() {
539
- clearSequenceTimer();
540
- sequenceTimer = setTimeout(function() {
541
- if (document.hidden) {
542
- pendingAutoPlay = true;
543
- return;
544
- }
545
- startPlayback();
546
- }, OPEN_DELAY_MS);
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
- if (document.readyState === 'complete') run();
550
- else window.addEventListener('load', run, { once: true });
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
- initReadProgressAndBackToTop(prefersReducedMotion);
18
- initNetworkCanvas(prefersReducedMotion);
19
- initHeroCanvas(prefersReducedMotion);
20
- if (document.querySelector('.rq-tv-stage')) {
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
  }
@@ -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
- .hero-stack {
31
- position: relative;
32
- aspect-ratio: 1020 / 510;
33
- background: #0a1218;
34
- }
35
- .hero-canvas-wrap {
36
- position: absolute;
37
- inset: 0;
38
- z-index: 1;
39
- opacity: 0;
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; }