PageSourceSearch

https://premluczak.work/assets/js/fade-controller.js?v=QdvcWOQ0kNes-WGh

js premluczak.work collected 2026-10-04 08:04:54 UTC 2,568 bytes, 78 lines download raw bytes

1/**
2 * Fade Controller - Manages container opacity transitions
3 */
4
5class FadeController {
6  constructor(element) {
7    this.element = typeof element === 'string' ? document.querySelector(element) : element;
8    this.currentOpacity = 0;
9    this.targetOpacity = 0;
10    this.duration = 0;
11    this.startTime = null;
12    this.easing = this.easeInOutCubic;
13  }
14
15  fadeIn(duration = 1500) {
16    this.animate(1, duration);
17  }
18
19  fadeOut(duration = 1500) {
20    this.animate(0, duration);
21  }
22
23  setOpacity(target, duration = 0) {
24    this.animate(target, duration);
25  }
26
27  animate(target, duration) {
28    // Sync from the element's actual current opacity before starting a new
29    // interpolation. this.currentOpacity is otherwise just an internal
30    // tracker that only ever gets updated by this class's OWN previous
31    // writes (starts at 0 from the constructor) — if the element's real
32    // opacity is different (its CSS/HTML default, or something else set it
33    // directly), the first animation frame below would snap the element to
34    // the stale tracked value before continuing toward target, instead of
35    // starting from wherever it visually already is. Confirmed cause of
36    // "particles abruptly disappear, then fade back in" on the very first
37    // fadeIn() call of a page load — the element defaulted to opacity 1
38    // (untouched), this.currentOpacity was still its constructor default
39    // of 0, so the first frame here forced a visible snap to 0.
40    const computed = parseFloat(getComputedStyle(this.element).opacity);
41    if (!Number.isNaN(computed)) this.currentOpacity = computed;
42
43    this.targetOpacity = target;
44    this.duration = duration;
45    this.startTime = Date.now();
46
47    if (duration === 0) {
48      this.currentOpacity = target;
49      this.element.style.opacity = target.toString();
50    } else {
51      this.updateFrame();
52    }
53  }
54
55  updateFrame() {
56    if (!this.startTime) return;
57
58    const elapsed = Date.now() - this.startTime;
59    const progress = Math.min(1, elapsed / this.duration);
60    const eased = this.easing(progress);
61
62    this.currentOpacity = this.currentOpacity + (this.targetOpacity - this.currentOpacity) * eased;
63    this.element.style.opacity = Math.max(0, Math.min(1, this.currentOpacity)).toString();
64
65    if (progress < 1) {
66      requestAnimationFrame(() => this.updateFrame());
67    }
68  }
69
70  easeInOutCubic(t) {
71    return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
72  }
73}
74
75// Export for browser
76if (typeof window !== 'undefined') {
77  window.FadeController = FadeController;
78}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.