@mirage-engine/core 0.3.20 → 0.3.22

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.
@@ -2,25 +2,28 @@ import { StyleData } from "@mirage-engine/dom-tracker";
2
2
  import { MeshRegistry } from "../store/MeshRegistry";
3
3
  import { Painter } from "@mirage-engine/painter";
4
4
  import * as THREE from "three";
5
+ import { WASM_STRIDE, OFFSET_LOCAL_X, OFFSET_LOCAL_Y, OFFSET_PARENT, OFFSET_WORLD_X, OFFSET_WORLD_Y } from "../types";
5
6
 
6
7
  export function animateMeshByData(
7
8
  registry: MeshRegistry,
8
9
  data: Map<HTMLElement, StyleData>,
10
+ sharedArray?: Float32Array,
9
11
  ) {
10
12
  if (data.size === 0) return;
11
13
 
14
+ const elementsToUpdateLayout = new Set<HTMLElement>();
15
+
12
16
  data.forEach((styleData, element) => {
13
17
  const mesh = registry.get(element);
14
18
  if (!mesh || !mesh.userData.basePosition) return;
15
19
 
20
+ // Fast-path styling updates (colors, opacity, etc.)
16
21
  Painter.forceUpdateUniforms(mesh.material as THREE.ShaderMaterial, {
17
22
  backgroundColor: styleData.backgroundColor,
18
23
  backgroundImage: styleData.backgroundImage,
19
24
  boxShadow: styleData.boxShadow,
20
25
  opacity: styleData.opacity,
21
- borderRadius:
22
- styleData.borderRadius ?? mesh.userData.baseStyles?.borderRadius,
23
- // width and height are no longer updated here, they are updated in syncMeshesByDOM
26
+ borderRadius: styleData.borderRadius ?? mesh.userData.baseStyles?.borderRadius,
24
27
  });
25
28
 
26
29
  if (mesh.userData.nativeMesh) {
@@ -31,6 +34,96 @@ export function animateMeshByData(
31
34
  opacity: styleData.opacity,
32
35
  borderRadius: styleData.borderRadius ?? mesh.userData.baseStyles?.borderRadius,
33
36
  });
37
+
38
+ if (styleData.scaleX !== undefined || styleData.scaleY !== undefined) {
39
+ const nativeMesh = mesh.userData.nativeMesh as THREE.Mesh;
40
+ const currentScaleX = nativeMesh.scale.x;
41
+ const currentScaleY = nativeMesh.scale.y;
42
+ const currentScaleZ = nativeMesh.scale.z;
43
+ nativeMesh.scale.set(
44
+ styleData.scaleX !== undefined ? styleData.scaleX * currentScaleX : currentScaleX,
45
+ styleData.scaleY !== undefined ? styleData.scaleY * currentScaleY : currentScaleY,
46
+ styleData.scaleZ !== undefined ? styleData.scaleZ * currentScaleZ : currentScaleZ
47
+ );
48
+ }
49
+ }
50
+
51
+ if (styleData.layoutChanged || styleData.width !== undefined || styleData.height !== undefined) {
52
+ elementsToUpdateLayout.add(element);
53
+ // Add all descendants that are in the registry
54
+ const descendants = element.querySelectorAll('*');
55
+ for (let i = 0; i < descendants.length; i++) {
56
+ const desc = descendants[i] as HTMLElement;
57
+ if (registry.has(desc)) {
58
+ elementsToUpdateLayout.add(desc);
59
+ }
60
+ }
61
+ } else if (styleData.x !== undefined || styleData.y !== undefined) {
62
+ // Just transform update (x, y) - doesn't affect child layout natively!
63
+ if (sharedArray && mesh.userData.wasmIndex !== undefined) {
64
+ const offset = mesh.userData.wasmIndex * WASM_STRIDE;
65
+ if (styleData.x !== undefined && mesh.userData.initialLocalX !== undefined) {
66
+ sharedArray[offset + OFFSET_LOCAL_X] = mesh.userData.initialLocalX + styleData.x;
67
+ }
68
+ if (styleData.y !== undefined && mesh.userData.initialLocalY !== undefined) {
69
+ sharedArray[offset + OFFSET_LOCAL_Y] = mesh.userData.initialLocalY + styleData.y;
70
+ }
71
+ }
72
+ }
73
+ });
74
+
75
+ // Now, process all layout updates efficiently!
76
+ elementsToUpdateLayout.forEach((element) => {
77
+ const mesh = registry.get(element);
78
+ if (!mesh) return;
79
+
80
+ const rect = element.getBoundingClientRect();
81
+
82
+ if (mesh.userData.domRect) {
83
+ mesh.userData.domRect.width = rect.width;
84
+ mesh.userData.domRect.height = rect.height;
85
+ }
86
+
87
+ let pad = (mesh.material as THREE.ShaderMaterial).userData?.shadowPadding || 0;
88
+ mesh.scale.set(rect.width + pad * 2, rect.height + pad * 2, 1);
89
+
90
+ Painter.forceUpdateUniforms(mesh.material as THREE.ShaderMaterial, {
91
+ width: rect.width,
92
+ height: rect.height,
93
+ });
94
+
95
+ if (sharedArray && mesh.userData.wasmIndex !== undefined) {
96
+ const offset = mesh.userData.wasmIndex * WASM_STRIDE;
97
+ const myWorldX = rect.left + (mesh.userData.isFixed ? 0 : window.scrollX);
98
+ const myWorldY = rect.top + (mesh.userData.isFixed ? 0 : window.scrollY);
99
+
100
+ const parentIndex = sharedArray[offset + OFFSET_PARENT];
101
+ let parentWorldX = 0;
102
+ let parentWorldY = 0;
103
+ if (parentIndex !== -1) {
104
+ const parentOffset = parentIndex * WASM_STRIDE;
105
+ parentWorldX = sharedArray[parentOffset + OFFSET_WORLD_X];
106
+ parentWorldY = sharedArray[parentOffset + OFFSET_WORLD_Y];
107
+ }
108
+
109
+ sharedArray[offset + OFFSET_LOCAL_X] = myWorldX - parentWorldX;
110
+ sharedArray[offset + OFFSET_LOCAL_Y] = myWorldY - parentWorldY;
111
+ }
112
+
113
+ if (mesh.userData.nativeMesh) {
114
+ const nativeMesh = mesh.userData.nativeMesh as THREE.Mesh;
115
+ let nPad = (nativeMesh.material as THREE.ShaderMaterial).userData?.shadowPadding || 0;
116
+ nativeMesh.scale.set(rect.width + nPad * 2, rect.height + nPad * 2, 1);
117
+
118
+ Painter.forceUpdateUniforms(nativeMesh.material as THREE.ShaderMaterial, {
119
+ width: rect.width,
120
+ height: rect.height,
121
+ });
122
+
123
+ if (mesh.userData.nativeRect) {
124
+ mesh.userData.nativeRect.width = rect.width;
125
+ mesh.userData.nativeRect.height = rect.height;
126
+ }
34
127
  }
35
128
  });
36
129
  }
@@ -47,8 +47,8 @@ export class Engine {
47
47
  this.syncer = new Syncer(this.target, this.renderer, this.registry, config);
48
48
  }
49
49
 
50
- public start() {
51
- this.syncer.start();
50
+ public async start() {
51
+ await this.syncer.start();
52
52
  }
53
53
 
54
54
  public stop() {
@@ -2,15 +2,18 @@ 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 { Visibility, USER_LAYER, ATTR_TRAVEL } from "../types";
5
+ import { Visibility, USER_LAYER, ATTR_TRAVEL, WASM_STRIDE } from "../types";
6
6
  import { animateMeshByData } from "../animation/Animator";
7
7
  import { Tracker } from "@mirage-engine/dom-tracker";
8
+ import { WasmSynchronizer } from "../wasm/WasmSynchronizer";
8
9
 
9
10
  export class Syncer {
10
11
  private target: HTMLElement;
11
12
  private renderer: Renderer;
12
13
  private registry: MeshRegistry;
13
14
  private isTravelEnabled: boolean = false;
15
+ private wasmSync: WasmSynchronizer;
16
+ private lastWasmNodeCount: number = 0;
14
17
 
15
18
  public tracker: Tracker;
16
19
 
@@ -23,6 +26,7 @@ export class Syncer {
23
26
  this.target = target;
24
27
  this.renderer = renderer;
25
28
  this.registry = registry;
29
+ this.wasmSync = new WasmSynchronizer();
26
30
 
27
31
  // Use Tracker from dom-tracker
28
32
  this.tracker = new Tracker(target, {
@@ -38,31 +42,55 @@ export class Syncer {
38
42
  this.renderer.createRenderTarget();
39
43
  }
40
44
 
45
+ const sharedArray = this.wasmSync.sharedArray;
46
+ this.renderer.updateScroll();
47
+ const extractContext = {
48
+ sharedArray: sharedArray || new Float32Array(),
49
+ currentIndex: 0,
50
+ scrollX: (this.renderer as any).getScrollX(),
51
+ scrollY: (this.renderer as any).getScrollY(),
52
+ };
53
+
41
54
  const sceneGraph = extractSceneGraph(
42
55
  this.target,
43
56
  pendingMask,
44
57
  USER_LAYER as Visibility,
45
58
  1,
46
59
  0,
47
- this.renderer.qualityFactor
60
+ this.renderer.qualityFactor,
61
+ undefined,
62
+ undefined,
63
+ undefined,
64
+ extractContext
48
65
  );
49
66
 
50
67
  if (sceneGraph) {
68
+ this.lastWasmNodeCount = extractContext.currentIndex;
51
69
  this.renderer.syncScene(sceneGraph, pendingDeletions);
70
+
71
+ if (sharedArray) {
72
+ this.renderer.saveInitialLocals(sharedArray);
73
+ }
52
74
  }
53
75
  });
54
76
 
55
77
  this.tracker.onStyleChange.add((pendingStyles) => {
56
- animateMeshByData(this.registry, pendingStyles);
78
+ animateMeshByData(this.registry, pendingStyles, this.wasmSync.sharedArray || undefined);
57
79
  });
58
80
 
59
81
  this.tracker.onRender.add(() => {
60
- this.renderer.syncMeshesByDOM();
82
+ this.renderer.updateScroll();
83
+ this.wasmSync.updatePhysics(this.lastWasmNodeCount);
84
+ if (this.wasmSync.sharedArray) {
85
+ this.renderer.syncMeshesByWasm(this.wasmSync.sharedArray);
86
+ }
61
87
  this.renderer.render();
62
88
  });
63
89
  }
64
90
 
65
- public start() {
91
+ public async start() {
92
+ // 10000개 노드 분량의 공유 메모리를 넉넉하게 선행 할당 (1-Pass 위함)
93
+ await this.wasmSync.initialize(10000 * WASM_STRIDE);
66
94
  this.tracker.start();
67
95
  }
68
96
 
@@ -14,6 +14,10 @@ import {
14
14
  ATTR_SELECT,
15
15
  ATTR_TRAVEL,
16
16
  ATTR_SHADER,
17
+ WASM_STRIDE,
18
+ OFFSET_PARENT,
19
+ OFFSET_LOCAL_X,
20
+ OFFSET_LOCAL_Y,
17
21
  } from "../types";
18
22
 
19
23
  import { BoxStyles, TextStyles, ShaderHooks } from "@mirage-engine/painter";
@@ -210,7 +214,15 @@ export function extractSceneGraph(
210
214
  inheritedNativeLayer?: number,
211
215
  inheritedNativeStyles?: any,
212
216
  inheritedClipElements?: HTMLElement[],
217
+ extractContext?: { sharedArray: Float32Array; currentIndex: number; scrollX?: number; scrollY?: number },
218
+ parentWasmIndex: number = -1,
219
+ parentWorldX: number = 0,
220
+ parentWorldY: number = 0,
221
+ inheritedIsFixed: boolean = false
213
222
  ): SceneNode | null {
223
+ const scrollX = extractContext?.scrollX ?? window.scrollX;
224
+ const scrollY = extractContext?.scrollY ?? window.scrollY;
225
+
214
226
  // Check text node
215
227
  if (sourceNode.nodeType === Node.TEXT_NODE) {
216
228
  const textNode = sourceNode as Text;
@@ -228,20 +240,34 @@ export function extractSceneGraph(
228
240
  const computed = parent ? window.getComputedStyle(parent) : null;
229
241
  if (!computed) return null;
230
242
 
243
+ const myIsFixed = inheritedIsFixed || computed.position === "fixed";
244
+
231
245
  // Calculate overall bounding box of the lines
232
246
  const minX = Math.min(...textLines.map((l) => l.rect.left));
233
247
  const minY = Math.min(...textLines.map((l) => l.rect.top));
234
248
  const maxX = Math.max(...textLines.map((l) => l.rect.left + l.rect.width));
235
249
  const maxY = Math.max(...textLines.map((l) => l.rect.top + l.rect.height));
236
250
 
251
+ const myWorldX = minX + (myIsFixed ? 0 : scrollX);
252
+ const myWorldY = minY + (myIsFixed ? 0 : scrollY);
253
+
254
+ let myWasmIndex = -1;
255
+ if (extractContext && extractContext.sharedArray) {
256
+ myWasmIndex = extractContext.currentIndex++;
257
+ const offset = myWasmIndex * WASM_STRIDE;
258
+ extractContext.sharedArray[offset + OFFSET_PARENT] = parentWasmIndex;
259
+ extractContext.sharedArray[offset + OFFSET_LOCAL_X] = myWorldX - parentWorldX;
260
+ extractContext.sharedArray[offset + OFFSET_LOCAL_Y] = myWorldY - parentWorldY;
261
+ }
262
+
237
263
  // Create SceneNode for the text node
238
264
  return {
239
265
  id: Math.random().toString(36).substring(2, 9),
240
266
  type: "TEXT",
241
267
  element: textNode as unknown as HTMLElement,
242
268
  rect: {
243
- x: minX + window.scrollX,
244
- y: minY + window.scrollY,
269
+ x: minX + (myIsFixed ? 0 : scrollX),
270
+ y: minY + (myIsFixed ? 0 : scrollY),
245
271
  width: maxX - minX,
246
272
  height: maxY - minY,
247
273
  },
@@ -264,8 +290,8 @@ export function extractSceneGraph(
264
290
  textLines: textLines.map((l) => ({
265
291
  text: l.text.trim(),
266
292
  rect: {
267
- x: l.rect.left + window.scrollX,
268
- y: l.rect.top + window.scrollY,
293
+ x: l.rect.left + (myIsFixed ? 0 : scrollX),
294
+ y: l.rect.top + (myIsFixed ? 0 : scrollY),
269
295
  width: l.rect.width,
270
296
  height: l.rect.height,
271
297
  },
@@ -275,7 +301,7 @@ export function extractSceneGraph(
275
301
  visibility: inheritedFlow,
276
302
  isTraveler: false,
277
303
  captureLayer,
278
- isFixed: computed.position === "fixed",
304
+ isFixed: myIsFixed,
279
305
  nativeLayer: inheritedNativeLayer,
280
306
  nativeStyles: inheritedNativeStyles
281
307
  ? {
@@ -299,13 +325,14 @@ export function extractSceneGraph(
299
325
  : undefined,
300
326
  nativeRect: inheritedNativeStyles
301
327
  ? {
302
- x: minX + window.scrollX,
303
- y: minY + window.scrollY,
328
+ x: minX + (myIsFixed ? 0 : scrollX),
329
+ y: minY + (myIsFixed ? 0 : scrollY),
304
330
  width: maxX - minX,
305
331
  height: maxY - minY,
306
332
  }
307
333
  : undefined,
308
334
  clipElements: inheritedClipElements,
335
+ wasmIndex: myWasmIndex !== -1 ? myWasmIndex : undefined,
309
336
  children: [],
310
337
  };
311
338
  }
@@ -498,6 +525,20 @@ export function extractSceneGraph(
498
525
  return null;
499
526
  }
500
527
 
528
+ const myIsFixed = inheritedIsFixed || computed.position === "fixed";
529
+
530
+ const myWorldX = rect.left + (myIsFixed ? 0 : scrollX);
531
+ const myWorldY = rect.top + (myIsFixed ? 0 : scrollY);
532
+
533
+ let myWasmIndex = -1;
534
+ if (extractContext && extractContext.sharedArray) {
535
+ myWasmIndex = extractContext.currentIndex++;
536
+ const offset = myWasmIndex * WASM_STRIDE;
537
+ extractContext.sharedArray[offset + OFFSET_PARENT] = parentWasmIndex;
538
+ extractContext.sharedArray[offset + OFFSET_LOCAL_X] = myWorldX - parentWorldX;
539
+ extractContext.sharedArray[offset + OFFSET_LOCAL_Y] = myWorldY - parentWorldY;
540
+ }
541
+
501
542
  // [TODO] dataset 방식으로 변경
502
543
  let id = element.getAttribute("data-mid");
503
544
  if (!id) {
@@ -511,64 +552,71 @@ export function extractSceneGraph(
511
552
  // console.log(`${element.id}: ${computed.background}`);
512
553
  // console.log(computed.backgroundImage);
513
554
  let imageSrc: string | undefined;
555
+ let nativeImageSrc: string | undefined;
514
556
  if (element.tagName === "IMG") {
515
557
  imageSrc = (element as HTMLImageElement).src;
516
558
  } else if (element.tagName.toLowerCase() === "svg") {
517
- const clone = element.cloneNode(true) as SVGSVGElement;
518
-
519
559
  const overrideColor = nativeParsedStyles?.color;
520
560
  const overrideFill = nativeParsedStyles?.fill;
521
561
  const overrideStroke = nativeParsedStyles?.stroke;
522
562
  const overrideOpacity = nativeParsedStyles?.opacity;
523
563
 
524
- const inlineSVGStyles = (orig: Element, cloned: Element) => {
525
- const computed = window.getComputedStyle(orig);
526
- const clonedHtml = cloned as HTMLElement;
564
+ const getSvgImageSrc = (useOverrides: boolean) => {
565
+ const clone = element.cloneNode(true) as SVGSVGElement;
566
+
567
+ const inlineSVGStyles = (orig: Element, cloned: Element) => {
568
+ const computed = window.getComputedStyle(orig);
569
+ const clonedHtml = cloned as HTMLElement;
527
570
 
528
- const isCurrentColorFill = computed.fill === computed.color;
529
- const isCurrentColorStroke = computed.stroke === computed.color;
571
+ const isCurrentColorFill = computed.fill === computed.color;
572
+ const isCurrentColorStroke = computed.stroke === computed.color;
530
573
 
531
- const fill = overrideFill || (isCurrentColorFill ? overrideColor : undefined) || computed.fill;
532
- if (fill && fill !== "none") clonedHtml.style.fill = fill;
574
+ const fill = (useOverrides ? (overrideFill || (isCurrentColorFill ? overrideColor : undefined)) : undefined) || computed.fill;
575
+ if (fill && fill !== "none") clonedHtml.style.fill = fill;
533
576
 
534
- const stroke = overrideStroke || (isCurrentColorStroke ? overrideColor : undefined) || computed.stroke;
535
- if (stroke && stroke !== "none") clonedHtml.style.stroke = stroke;
577
+ const stroke = (useOverrides ? (overrideStroke || (isCurrentColorStroke ? overrideColor : undefined)) : undefined) || computed.stroke;
578
+ if (stroke && stroke !== "none") clonedHtml.style.stroke = stroke;
536
579
 
537
- if (computed.strokeWidth && computed.strokeWidth !== "0px")
538
- clonedHtml.style.strokeWidth = computed.strokeWidth;
580
+ if (computed.strokeWidth && computed.strokeWidth !== "0px")
581
+ clonedHtml.style.strokeWidth = computed.strokeWidth;
539
582
 
540
- const color = overrideColor || computed.color;
541
- if (color) clonedHtml.style.color = color;
583
+ const color = (useOverrides ? overrideColor : undefined) || computed.color;
584
+ if (color) clonedHtml.style.color = color;
542
585
 
543
- const opacity = overrideOpacity || computed.opacity;
544
- if (opacity && opacity !== "1") clonedHtml.style.opacity = opacity;
586
+ const opacity = (useOverrides ? overrideOpacity : undefined) || computed.opacity;
587
+ if (opacity && opacity !== "1") clonedHtml.style.opacity = opacity;
545
588
 
546
- for (let i = 0; i < orig.children.length; i++) {
547
- inlineSVGStyles(orig.children[i], cloned.children[i]);
548
- }
549
- };
589
+ for (let i = 0; i < orig.children.length; i++) {
590
+ inlineSVGStyles(orig.children[i], cloned.children[i]);
591
+ }
592
+ };
550
593
 
551
- inlineSVGStyles(element, clone);
594
+ inlineSVGStyles(element, clone);
552
595
 
553
- const svgRect = element.getBoundingClientRect();
554
- const scale = window.devicePixelRatio * qualityFactor; // High-DPI 대응을 위한 해상도 스케일업
596
+ const svgRect = element.getBoundingClientRect();
597
+ const scale = window.devicePixelRatio * qualityFactor;
555
598
 
556
- if (!clone.hasAttribute("viewBox")) {
557
- clone.setAttribute("viewBox", `0 0 ${svgRect.width} ${svgRect.height}`);
558
- }
599
+ if (!clone.hasAttribute("viewBox")) {
600
+ clone.setAttribute("viewBox", `0 0 ${svgRect.width} ${svgRect.height}`);
601
+ }
559
602
 
560
- clone.setAttribute("width", (svgRect.width * scale).toString());
561
- clone.setAttribute("height", (svgRect.height * scale).toString());
603
+ clone.setAttribute("width", (svgRect.width * scale).toString());
604
+ clone.setAttribute("height", (svgRect.height * scale).toString());
562
605
 
563
- let svgString = new XMLSerializer().serializeToString(clone);
564
- if (!svgString.includes("xmlns=")) {
565
- svgString = svgString.replace(
566
- "<svg",
567
- '<svg xmlns="http://www.w3.org/2000/svg"',
568
- );
569
- }
606
+ let svgString = new XMLSerializer().serializeToString(clone);
607
+ if (!svgString.includes("xmlns=")) {
608
+ svgString = svgString.replace(
609
+ "<svg",
610
+ '<svg xmlns="http://www.w3.org/2000/svg"',
611
+ );
612
+ }
613
+ return `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
614
+ };
570
615
 
571
- imageSrc = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`;
616
+ imageSrc = getSvgImageSrc(false);
617
+ if (nativeLayer !== undefined && (overrideColor || overrideFill || overrideStroke || overrideOpacity)) {
618
+ nativeImageSrc = getSvgImageSrc(true);
619
+ }
572
620
  } else if (computed.backgroundImage && computed.backgroundImage !== "none") {
573
621
  const match = computed.backgroundImage.match(/url\(['"]?(.*?)['"]?\)/);
574
622
  if (match) {
@@ -620,6 +668,11 @@ export function extractSceneGraph(
620
668
  ? nativeParsedStyles
621
669
  : undefined,
622
670
  nextClipElements,
671
+ extractContext,
672
+ myWasmIndex,
673
+ myWorldX,
674
+ myWorldY,
675
+ myIsFixed
623
676
  );
624
677
  if (childNode) {
625
678
  children.push(childNode);
@@ -632,8 +685,8 @@ export function extractSceneGraph(
632
685
  type: "BOX",
633
686
  element,
634
687
  rect: {
635
- x: rect.left + window.scrollX,
636
- y: rect.top + window.scrollY,
688
+ x: rect.left + scrollX,
689
+ y: rect.top + scrollY,
637
690
  width: rect.width,
638
691
  height: rect.height,
639
692
  },
@@ -668,6 +721,7 @@ export function extractSceneGraph(
668
721
  nativeParsedStyles.boxShadow ?? baseStyles.boxShadow,
669
722
  isTraveler: baseStyles.isTraveler,
670
723
  transform: nativeParsedStyles.transform,
724
+ imageSrc: nativeImageSrc ?? imageSrc,
671
725
  }
672
726
  : undefined,
673
727
  nativeRect:
@@ -676,11 +730,11 @@ export function extractSceneGraph(
676
730
  x:
677
731
  nativeParsedStyles.x !== undefined
678
732
  ? parseFloat(nativeParsedStyles.x)
679
- : rect.left + window.scrollX,
733
+ : rect.left + scrollX,
680
734
  y:
681
735
  nativeParsedStyles.y !== undefined
682
736
  ? parseFloat(nativeParsedStyles.y)
683
- : rect.top + window.scrollY,
737
+ : rect.top + scrollY,
684
738
  width:
685
739
  nativeParsedStyles.width !== undefined
686
740
  ? parseFloat(nativeParsedStyles.width)
@@ -691,8 +745,9 @@ export function extractSceneGraph(
691
745
  : rect.height,
692
746
  }
693
747
  : undefined,
694
- isFixed: computed.position === "fixed",
748
+ isFixed: myIsFixed,
695
749
  clipElements: inheritedClipElements,
750
+ wasmIndex: myWasmIndex !== -1 ? myWasmIndex : undefined,
696
751
  children,
697
752
  shaderHooks,
698
753
  };
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { Engine} from "./core/Engine";
2
2
  export * from "./types";
3
+ export * from "./wasm/WasmSynchronizer";