@mirage-engine/core 0.2.2 → 0.3.1

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.
@@ -1,4 +1,4 @@
1
- import { CoreConfig } from "../types";
1
+ import { CoreConfig, ATTR_DOM } from "../types";
2
2
  import { Renderer } from "../renderer/Renderer";
3
3
  import { Syncer } from "./Syncer";
4
4
  import { MeshRegistry } from "../store/MeshRegistry";
@@ -17,7 +17,7 @@ export class Engine {
17
17
  const style = document.createElement("style");
18
18
  style.id = "mirage-engine-styles";
19
19
  style.textContent = `
20
- [data-mirage-dom="hide"] {
20
+ [${ATTR_DOM.NAME}="${ATTR_DOM.VALUES.HIDE}"] {
21
21
  opacity: 0 !important;
22
22
  }
23
23
  `;
@@ -59,6 +59,18 @@ export class Engine {
59
59
  this.renderer.dispose();
60
60
  }
61
61
 
62
+ public getTracker() {
63
+ return this.syncer.tracker;
64
+ }
65
+
66
+ public updateUniforms(element: HTMLElement, uniforms: Record<string, any>) {
67
+ this.renderer.updateUniforms(element, uniforms);
68
+ }
69
+
70
+ public getCanvas() {
71
+ return this.renderer.canvas;
72
+ }
73
+
62
74
  public test() {
63
75
  const boxMesh = this.registry.get(
64
76
  document.querySelector("#box2") as HTMLElement,
@@ -2,53 +2,17 @@ import { CoreConfig } from "../types/config";
2
2
  import { Renderer } from "../renderer/Renderer";
3
3
  import { MeshRegistry } from "../store/MeshRegistry";
4
4
  import { extractSceneGraph } from "../dom/Extractor";
5
- import {
6
- DIRTY_NONE,
7
- DIRTY_RECT,
8
- DIRTY_STRUCTURE,
9
- DIRTY_CONTENT
10
- ,
11
- DIRTY_STYLE,
12
- USER_LAYER,
13
- SYSTEM_LAYER,
14
- Visibility,
15
- StyleData,
16
- } from "../types";
17
- import { extractFromStyle, animateMeshByData, updateFixedMeshesScroll } from "../animation/Animator";
18
-
19
- interface InternalResizeConfig {
20
- enabled: boolean;
21
- delay: number;
22
- onStart?: () => void;
23
- onEnd?: () => void;
24
- }
5
+ import { Visibility, USER_LAYER, ATTR_TRAVEL } from "../types";
6
+ import { animateMeshByData, updateFixedMeshesScroll } from "../animation/Animator";
7
+ import { Tracker } from "@mirage-engine/dom-tracker";
25
8
 
26
9
  export class Syncer {
27
10
  private target: HTMLElement;
28
11
  private renderer: Renderer;
29
12
  private registry: MeshRegistry;
30
- private filter: CoreConfig["filter"];
31
-
32
- private observer: MutationObserver;
33
- private pendingDeletions: Set<HTMLElement> = new Set();
34
- private pendingStyles: Map<HTMLElement, StyleData> = new Map();
35
-
36
- private isDomDirty: boolean = false;
37
- private isRunning: boolean = false;
38
13
  private isTravelEnabled: boolean = false;
39
-
40
- private pendingMask: number = DIRTY_NONE;
41
-
42
- private mutationTimer: number | null = null;
43
- private cssTimer: number | null = null;
44
-
45
- private resizeConfig: InternalResizeConfig;
46
- private resizeTimer: number | null = null;
47
- private isResizing: boolean = false;
48
-
49
- private lastScrollX: number = 0;
50
- private lastScrollY: number = 0;
51
- private scrollTimer: number | null = null;
14
+
15
+ public tracker: Tracker;
52
16
 
53
17
  constructor(
54
18
  target: HTMLElement,
@@ -59,213 +23,53 @@ export class Syncer {
59
23
  this.target = target;
60
24
  this.renderer = renderer;
61
25
  this.registry = registry;
62
- this.filter = config.filter;
63
-
64
- // Resize Debounce Configuration
65
- const debounceOpt = config.resizeDebounce ?? true;
66
- if (debounceOpt === false) {
67
- this.resizeConfig = { enabled: false, delay: 0 };
68
- } else if (debounceOpt === true) {
69
- this.resizeConfig = { enabled: true, delay: 150 };
70
- } else {
71
- this.resizeConfig = {
72
- enabled: true,
73
- delay: debounceOpt.delay ?? 150,
74
- onStart: debounceOpt.onStart,
75
- onEnd: debounceOpt.onEnd,
76
- };
77
- }
78
-
79
- this.observer = new MutationObserver((mutations) => {
80
- let currentMask = DIRTY_NONE;
81
26
 
82
- for (const mutation of mutations) {
83
- if (mutation.type === "childList") {
84
- currentMask |= DIRTY_STRUCTURE;
85
- if (mutation.removedNodes.length > 0) {
86
- mutation.removedNodes.forEach((node) => {
87
- if (node instanceof HTMLElement) {
88
- this.pendingDeletions.add(node);
89
- }
90
- });
91
- }
92
- } else if (mutation.type === "attributes") {
93
- if (mutation.attributeName === "style") {
94
- currentMask |= DIRTY_RECT | DIRTY_STYLE;
95
- const target = mutation.target as HTMLElement;
27
+ // Use Tracker from dom-tracker
28
+ this.tracker = new Tracker(target, {
29
+ resizeDebounce: config.resizeDebounce,
30
+ });
96
31
 
97
- const extractedStyleData = extractFromStyle(target.style);
98
- // console.log(extractedStyleData);
99
- this.pendingStyles.set(target, extractedStyleData);
100
- } else if (mutation.attributeName === "class") {
101
- currentMask |= DIRTY_RECT | DIRTY_STYLE;
102
- }
103
- // else if (mutation.attributeName === "data-mirage") {
104
- // }
105
- } else if (mutation.type === "characterData") {
106
- currentMask |= DIRTY_CONTENT | DIRTY_RECT;
107
- }
32
+ // Wire up the hooks
33
+ this.tracker.onLayoutChange.add((pendingMask, pendingDeletions) => {
34
+ const discoveredTraveler =
35
+ document.querySelector(`[${ATTR_TRAVEL.NAME}~='${ATTR_TRAVEL.VALUES.TRAVELER}']`) !== null;
36
+ if (discoveredTraveler && !this.isTravelEnabled) {
37
+ this.isTravelEnabled = true;
38
+ this.renderer.createRenderTarget();
108
39
  }
109
40
 
110
- if (currentMask !== DIRTY_NONE) {
111
- this.pendingMask |= currentMask;
112
-
113
- // Structural Change detected
114
- if (currentMask & DIRTY_STRUCTURE) {
115
- this.clearTimers();
116
- this.isDomDirty = true;
117
- return;
118
- }
119
- if (this.mutationTimer) {
120
- clearTimeout(this.mutationTimer);
121
- }
122
- this.mutationTimer = window.setTimeout(() => {
123
- this.mutationTimer = null;
124
- this.isDomDirty = true;
125
- }, 200);
41
+ const sceneGraph = extractSceneGraph(
42
+ this.target,
43
+ pendingMask,
44
+ USER_LAYER as Visibility,
45
+ 1,
46
+ 0,
47
+ this.renderer.qualityFactor
48
+ );
49
+
50
+ if (sceneGraph) {
51
+ this.renderer.syncScene(sceneGraph, pendingDeletions);
126
52
  }
127
53
  });
128
- }
129
-
130
- public start() {
131
- if (this.isRunning) return;
132
- this.isRunning = true;
133
54
 
134
- //DOM observing start
135
- this.observer.observe(this.target, {
136
- childList: true,
137
- subtree: true,
138
- attributes: true,
139
- characterData: true,
55
+ this.tracker.onScrollChange.add((scrollX, scrollY) => {
56
+ updateFixedMeshesScroll(this.renderer.fixedMeshes, scrollX, scrollY);
140
57
  });
141
58
 
142
- this.target.addEventListener("transitionend", this.onTransitionFinished);
143
- this.target.addEventListener("animationend", this.onTransitionFinished);
144
- window.addEventListener("resize", this.onWindowResize);
145
-
146
- this.forceUpdateScene();
147
- this.renderLoop();
148
- // for debugging
149
- // this.renderer.showScissoredRenderTarget();
150
- }
151
-
152
- public stop() {
153
- this.isRunning = false;
154
- this.observer.disconnect();
155
-
156
- this.clearTimers();
59
+ this.tracker.onStyleChange.add((pendingStyles) => {
60
+ animateMeshByData(this.registry, pendingStyles);
61
+ });
157
62
 
158
- this.target.removeEventListener("transitionend", this.onTransitionFinished);
159
- this.target.removeEventListener("animationend", this.onTransitionFinished);
160
- window.removeEventListener("resize", this.onWindowResize);
63
+ this.tracker.onRender.add(() => {
64
+ this.renderer.render();
65
+ });
161
66
  }
162
67
 
163
- private clearTimers() {
164
- if (this.mutationTimer) {
165
- clearTimeout(this.mutationTimer);
166
- this.mutationTimer = null;
167
- }
168
- if (this.cssTimer) {
169
- clearTimeout(this.cssTimer);
170
- this.cssTimer = null;
171
- }
172
- if (this.scrollTimer) {
173
- clearTimeout(this.scrollTimer);
174
- this.scrollTimer = null;
175
- }
68
+ public start() {
69
+ this.tracker.start();
176
70
  }
177
71
 
178
- private onTransitionFinished = (e: Event) => {
179
- if (!this.target.contains(e.target as Node)) return;
180
- if (this.mutationTimer !== null) return;
181
- if (this.cssTimer) clearTimeout(this.cssTimer);
182
- if (this.pendingStyles.size != 0) return;
183
- this.pendingMask |= DIRTY_RECT | DIRTY_STYLE;
184
- this.cssTimer = window.setTimeout(() => {
185
- this.isDomDirty = true;
186
- this.cssTimer = null;
187
- }, 50);
188
- };
189
-
190
- private onWindowResize = () => {
191
- if (!this.resizeConfig.enabled) {
192
- this.isDomDirty = true;
193
- return;
194
- }
195
- if (!this.isResizing) {
196
- this.isResizing = true;
197
- if (this.resizeConfig.onStart) this.resizeConfig.onStart();
198
- }
199
- if (this.resizeTimer) clearTimeout(this.resizeTimer);
200
- this.resizeTimer = window.setTimeout(() => {
201
- this.isDomDirty = true;
202
- if (this.resizeConfig.onEnd) this.resizeConfig.onEnd();
203
- this.isResizing = false;
204
- this.resizeTimer = null;
205
- }, this.resizeConfig.delay);
206
- };
207
-
208
- private forceUpdateScene() {
209
- this.isDomDirty = false;
210
-
211
- this.lastScrollX = window.scrollX;
212
- this.lastScrollY = window.scrollY;
213
-
214
- const discoveredTraveler =
215
- document.querySelector("[data-mirage-travel='traveler']") !== null;
216
- if (discoveredTraveler && !this.isTravelEnabled) {
217
- this.isTravelEnabled = true;
218
- this.renderer.createRenderTarget();
219
- }
220
-
221
- const sceneGraph = extractSceneGraph(
222
- this.target,
223
- this.pendingMask,
224
- (discoveredTraveler
225
- ? USER_LAYER | SYSTEM_LAYER
226
- : USER_LAYER) as Visibility,
227
- this.filter,
228
- );
229
-
230
- if (sceneGraph) {
231
- this.renderer.syncScene(sceneGraph, this.pendingDeletions);
232
- this.pendingDeletions.clear();
233
- }
234
-
235
- this.pendingMask = DIRTY_NONE;
72
+ public stop() {
73
+ this.tracker.stop();
236
74
  }
237
-
238
- private renderLoop = () => {
239
- if (!this.isRunning) return;
240
-
241
- if (this.isDomDirty) {
242
- this.forceUpdateScene();
243
- }
244
-
245
- const currentScrollX = window.scrollX;
246
- const currentScrollY = window.scrollY;
247
- if (currentScrollX !== this.lastScrollX || currentScrollY !== this.lastScrollY) {
248
- updateFixedMeshesScroll(this.renderer.fixedMeshes, currentScrollX, currentScrollY);
249
-
250
- this.lastScrollX = currentScrollX;
251
- this.lastScrollY = currentScrollY;
252
-
253
- if (this.scrollTimer) clearTimeout(this.scrollTimer);
254
- this.scrollTimer = window.setTimeout(() => {
255
- this.pendingMask |= DIRTY_RECT;
256
- this.isDomDirty = true;
257
- this.scrollTimer = null;
258
- }, 150);
259
- }
260
-
261
- if (this.pendingStyles.size > 0) {
262
- animateMeshByData(this.registry, this.pendingStyles);
263
- this.pendingStyles.clear();
264
- }
265
-
266
- this.renderer.render();
267
- requestAnimationFrame(this.renderLoop);
268
- };
269
-
270
- // [ThinkPoint] change call back pattern when after
271
75
  }