@vectojs/core 1.12.0 → 1.13.0
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/dist/index.js +142 -86
- package/dist/index.mjs +56 -0
- package/dist/tree/Scene.d.ts +52 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -667,16 +667,29 @@ var Scene = (_class2 = class _Scene {
|
|
|
667
667
|
__init10() {this.dirty = true}
|
|
668
668
|
/** Whether to throttle rendering to 2 FPS when the scene is static to save power. */
|
|
669
669
|
__init11() {this.autoThrottle = true}
|
|
670
|
+
// --- Frame telemetry (read via `frameStats`) ---------------------------
|
|
671
|
+
/** Wall-clock ms spent inside the last `render()` call. */
|
|
672
|
+
__init12() {this._lastFrameMs = 0}
|
|
673
|
+
/** Rolling exponential average of rendered-frame intervals, in ms. */
|
|
674
|
+
__init13() {this._avgFrameIntervalMs = 0}
|
|
675
|
+
/** dt (ms) handed to the last rendered frame. */
|
|
676
|
+
__init14() {this._lastDt = 0}
|
|
677
|
+
/** Count of frames actually rendered since the loop started. */
|
|
678
|
+
__init15() {this._renderedFrames = 0}
|
|
679
|
+
/** Count of rAF ticks skipped (idle / capped) since the loop started. */
|
|
680
|
+
__init16() {this._skippedFrames = 0}
|
|
681
|
+
/** `time` of the previous *rendered* frame, for interval measurement. */
|
|
682
|
+
__init17() {this._lastRenderTick = 0}
|
|
670
683
|
/**
|
|
671
684
|
* Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
|
|
672
685
|
* the loop renders at most `maxFPS` times per second; animations still run,
|
|
673
686
|
* just less often. See {@link SceneOptions.maxFPS}.
|
|
674
687
|
*/
|
|
675
|
-
|
|
688
|
+
__init18() {this.maxFPS = 60}
|
|
676
689
|
/** Whether the OS prefers-reduced-motion setting auto-caps the loop. */
|
|
677
|
-
|
|
690
|
+
__init19() {this.respectReducedMotion = true}
|
|
678
691
|
/** Cached media-query list; `.matches` is read live each frame. */
|
|
679
|
-
|
|
692
|
+
__init20() {this.reducedMotionQuery = null}
|
|
680
693
|
/** True when the OS asks for reduced motion and we respect it. Read by the animation drivers. */
|
|
681
694
|
get prefersReducedMotion() {
|
|
682
695
|
return this.respectReducedMotion && !!_optionalChain([this, 'access', _21 => _21.reducedMotionQuery, 'optionalAccess', _22 => _22.matches]);
|
|
@@ -685,95 +698,95 @@ var Scene = (_class2 = class _Scene {
|
|
|
685
698
|
* Throttle interval (ms) for the a11y/automation shadow sync. `0` = every
|
|
686
699
|
* frame. See {@link SceneOptions.a11ySyncInterval}.
|
|
687
700
|
*/
|
|
688
|
-
|
|
701
|
+
__init21() {this.a11ySyncInterval = 0}
|
|
689
702
|
/** Timestamp of the last a11y sync, for throttling. */
|
|
690
|
-
|
|
703
|
+
__init22() {this.lastA11ySync = -Infinity}
|
|
691
704
|
/** True if we skipped an a11y sync during animation and need to sync when at rest. */
|
|
692
|
-
|
|
705
|
+
__init23() {this.a11yPendingSyncAfterAnimation = false}
|
|
693
706
|
// A11y / Automation Layer. `null` in non-DOM (SSR/Node) environments — the
|
|
694
707
|
// whole projection degrades to a no-op so the engine's logic stays usable
|
|
695
708
|
// server-side (e.g. headless layout / vector export) without jsdom.
|
|
696
709
|
|
|
697
|
-
|
|
710
|
+
__init24() {this.a11yElements = /* @__PURE__ */ new Map()}
|
|
698
711
|
/** DOM nodes mirroring static text content, keyed by entity id. */
|
|
699
|
-
|
|
712
|
+
__init25() {this.contentElements = /* @__PURE__ */ new Map()}
|
|
700
713
|
/** Pending cold font-calibration frame per projected grid entity. */
|
|
701
|
-
|
|
714
|
+
__init26() {this.contentGridCalibrationFrames = /* @__PURE__ */ new Map()}
|
|
702
715
|
/** Detached, untransformed font probes used by the cold calibration pass. */
|
|
703
|
-
|
|
716
|
+
__init27() {this.contentGridCalibrationProbes = /* @__PURE__ */ new Map()}
|
|
704
717
|
/** Invalidates grid font calibration after browser font availability changes. */
|
|
705
|
-
|
|
718
|
+
__init28() {this.contentFontEpoch = 0}
|
|
706
719
|
/** Cached Canvas-to-client scale for the current font/viewport epoch. */
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
720
|
+
__init29() {this.contentMetricScaleEpoch = -1}
|
|
721
|
+
__init30() {this.contentMetricScaleX = 1}
|
|
722
|
+
__init31() {this.contentProjectionEnabled = true}
|
|
710
723
|
/**
|
|
711
724
|
* True while a text-selection drag that started on a projection's blank
|
|
712
725
|
* region (no text node under the press) is being driven manually — the
|
|
713
726
|
* browser has no native anchor for it, so mousemove extends the Selection
|
|
714
727
|
* from the position we resolved ourselves.
|
|
715
728
|
*/
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
729
|
+
__init32() {this.blankRegionSelectionDrag = false}
|
|
730
|
+
__init33() {this.contentSelectionAnchor = null}
|
|
731
|
+
__init34() {this.contentSelectionEndListener = null}
|
|
719
732
|
// Animation/interactive flags collected during the render walk (tree-walk
|
|
720
733
|
// fusion): the loop reads last frame's answers instead of re-walking the
|
|
721
734
|
// tree up to 4× per tick. Start true so the first tick stays conservative.
|
|
722
|
-
|
|
723
|
-
|
|
735
|
+
__init35() {this.frameHadAnimation = true}
|
|
736
|
+
__init36() {this.frameHadInteractive = true}
|
|
724
737
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
738
|
+
__init37() {this.focusedA11yElement = null}
|
|
739
|
+
__init38() {this.caretBlinkTimer = null}
|
|
740
|
+
__init39() {this.a11yNeedsReorder = true}
|
|
741
|
+
__init40() {this.portalRoot = null}
|
|
742
|
+
__init41() {this.fullViewportElements = []}
|
|
743
|
+
__init42() {this.normalElements = []}
|
|
744
|
+
__init43() {this.activeIds = /* @__PURE__ */ new Set()}
|
|
745
|
+
__init44() {this.activePortalsThisFrame = /* @__PURE__ */ new Set()}
|
|
746
|
+
__init45() {this.activePortalsPrevFrame = /* @__PURE__ */ new Set()}
|
|
747
|
+
__init46() {this.portalEntities = /* @__PURE__ */ new Map()}
|
|
748
|
+
__init47() {this.renderOrderCounter = 0}
|
|
736
749
|
/**
|
|
737
750
|
* Authoritative paint order for semantic nodes discovered during the main
|
|
738
751
|
* render. A node may not have a DOM projection until the following a11y
|
|
739
752
|
* sync, so retaining the order prevents a newly opened overlay from spending
|
|
740
753
|
* its first frame below previously projected controls.
|
|
741
754
|
*/
|
|
742
|
-
|
|
755
|
+
__init48() {this.a11yRenderOrders = /* @__PURE__ */ new Map()}
|
|
743
756
|
// Optional WebGL point-cloud layer (see SceneOptions.pointBackend).
|
|
744
|
-
|
|
745
|
-
|
|
757
|
+
__init49() {this.pointRenderer = null}
|
|
758
|
+
__init50() {this.glCanvas = null}
|
|
746
759
|
|
|
747
760
|
|
|
748
761
|
|
|
749
|
-
|
|
762
|
+
__init51() {this.disableWindowResize = false}
|
|
750
763
|
/** See {@link SceneOptions.maxDPR}. `undefined` = uncapped (real DPR). */
|
|
751
764
|
|
|
752
765
|
// WebGPU properties
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
766
|
+
__init52() {this.destroyed = false}
|
|
767
|
+
__init53() {this.device = null}
|
|
768
|
+
__init54() {this.deviceLost = false}
|
|
769
|
+
__init55() {this.particleBackend = "auto"}
|
|
770
|
+
__init56() {this._webgpuDisabled = false}
|
|
758
771
|
get webgpuDisabled() {
|
|
759
772
|
return this._webgpuDisabled || this.particleBackend === "cpu";
|
|
760
773
|
}
|
|
761
774
|
set webgpuDisabled(value) {
|
|
762
775
|
this._webgpuDisabled = value;
|
|
763
776
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
777
|
+
__init57() {this.recoveryTimerId = null}
|
|
778
|
+
__init58() {this.manager = null}
|
|
779
|
+
__init59() {this.initializingWebGPU = false}
|
|
780
|
+
__init60() {this.gpuCanvas = null}
|
|
781
|
+
__init61() {this.gpuContext = null}
|
|
769
782
|
/** True while the GPU canvas holds a presented particle frame (needs clearing when they leave). */
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
783
|
+
__init62() {this.gpuHasContent = false}
|
|
784
|
+
__init63() {this.mouseX = -9999}
|
|
785
|
+
__init64() {this.mouseY = -9999}
|
|
786
|
+
__init65() {this.pointerMoveListener = null}
|
|
787
|
+
__init66() {this.pointerLeaveListener = null}
|
|
788
|
+
__init67() {this.hasWarnedZeroSize = false}
|
|
789
|
+
__init68() {this.fontLoadHandler = null}
|
|
777
790
|
// ── Dev-mode warning infrastructure ──────────────────────────────
|
|
778
791
|
//
|
|
779
792
|
// Enable with `Scene.devMode = true` or by setting `globalThis.__DEV__`.
|
|
@@ -791,7 +804,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
791
804
|
return false;
|
|
792
805
|
}
|
|
793
806
|
|
|
794
|
-
|
|
807
|
+
__init69() {this._devFrameCount = 0}
|
|
795
808
|
_devWarn(message) {
|
|
796
809
|
if (!this._devActive) return;
|
|
797
810
|
console.warn(`[vectojs/dev] ${message}`);
|
|
@@ -835,7 +848,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
835
848
|
};
|
|
836
849
|
walkProjections(this.root);
|
|
837
850
|
}
|
|
838
|
-
constructor(canvas, options = {}) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);_class2.prototype.__init42.call(this);_class2.prototype.__init43.call(this);_class2.prototype.__init44.call(this);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);_class2.prototype.__init48.call(this);_class2.prototype.__init49.call(this);_class2.prototype.__init50.call(this);_class2.prototype.__init51.call(this);_class2.prototype.__init52.call(this);_class2.prototype.__init53.call(this);_class2.prototype.__init54.call(this);_class2.prototype.__init55.call(this);_class2.prototype.__init56.call(this);_class2.prototype.__init57.call(this);_class2.prototype.__init58.call(this);_class2.prototype.__init59.call(this);_class2.prototype.__init60.call(this);_class2.prototype.__init61.call(this);_class2.prototype.__init62.call(this);_class2.prototype.__init63.call(this);
|
|
851
|
+
constructor(canvas, options = {}) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);_class2.prototype.__init42.call(this);_class2.prototype.__init43.call(this);_class2.prototype.__init44.call(this);_class2.prototype.__init45.call(this);_class2.prototype.__init46.call(this);_class2.prototype.__init47.call(this);_class2.prototype.__init48.call(this);_class2.prototype.__init49.call(this);_class2.prototype.__init50.call(this);_class2.prototype.__init51.call(this);_class2.prototype.__init52.call(this);_class2.prototype.__init53.call(this);_class2.prototype.__init54.call(this);_class2.prototype.__init55.call(this);_class2.prototype.__init56.call(this);_class2.prototype.__init57.call(this);_class2.prototype.__init58.call(this);_class2.prototype.__init59.call(this);_class2.prototype.__init60.call(this);_class2.prototype.__init61.call(this);_class2.prototype.__init62.call(this);_class2.prototype.__init63.call(this);_class2.prototype.__init64.call(this);_class2.prototype.__init65.call(this);_class2.prototype.__init66.call(this);_class2.prototype.__init67.call(this);_class2.prototype.__init68.call(this);_class2.prototype.__init69.call(this);
|
|
839
852
|
this.canvas = canvas;
|
|
840
853
|
this.debugA11y = _nullishCoalesce(options.debugA11y, () => ( false));
|
|
841
854
|
this.disableWindowResize = _nullishCoalesce(options.disableWindowResize, () => ( false));
|
|
@@ -1318,6 +1331,36 @@ var Scene = (_class2 = class _Scene {
|
|
|
1318
1331
|
markDirty() {
|
|
1319
1332
|
this.dirty = true;
|
|
1320
1333
|
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Live frame telemetry for profilers and devtools overlays. All timings are
|
|
1336
|
+
* measured on the `requestAnimationFrame` loop; a scene driven only by
|
|
1337
|
+
* {@link step} (e.g. deterministic video export) leaves these at their zero
|
|
1338
|
+
* defaults.
|
|
1339
|
+
*
|
|
1340
|
+
* `fps` is derived from the interval between *rendered* frames, so idle
|
|
1341
|
+
* `onDemand` scenes and frames skipped by the {@link maxFPS} cap or the
|
|
1342
|
+
* static auto-throttle do not deflate it — it reports the cadence of actual
|
|
1343
|
+
* redraws, not the raw rAF rate. `frameTimeMs` is the wall-clock cost of the
|
|
1344
|
+
* last `render()` pass alone (excludes a11y/content-projection sync).
|
|
1345
|
+
*
|
|
1346
|
+
* The renderer always repaints the full canvas, so there is no partial
|
|
1347
|
+
* dirty-rectangle to expose; `dirty` is the boolean redraw-pending flag and
|
|
1348
|
+
* `pendingRedraw` reflects whether the next `onDemand` tick will actually
|
|
1349
|
+
* render.
|
|
1350
|
+
*/
|
|
1351
|
+
get frameStats() {
|
|
1352
|
+
const interval = this._avgFrameIntervalMs;
|
|
1353
|
+
return {
|
|
1354
|
+
fps: interval > 0 ? Math.min(1e3 / interval, this.maxFPS > 0 ? this.maxFPS : 1e3 / interval) : 0,
|
|
1355
|
+
frameTimeMs: this._lastFrameMs,
|
|
1356
|
+
frameIntervalMs: interval,
|
|
1357
|
+
dt: this._lastDt,
|
|
1358
|
+
renderedFrames: this._renderedFrames,
|
|
1359
|
+
skippedFrames: this._skippedFrames,
|
|
1360
|
+
renderMode: this.renderMode,
|
|
1361
|
+
dirty: this.dirty
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1321
1364
|
/** True when any node in the subtree has a pending animation. */
|
|
1322
1365
|
/** True when any node in the subtree is interactive (drives a11y sync). */
|
|
1323
1366
|
syncOptionalAttribute(element, name, value) {
|
|
@@ -2221,6 +2264,7 @@ var Scene = (_class2 = class _Scene {
|
|
|
2221
2264
|
cap = Math.min(cap, 2);
|
|
2222
2265
|
}
|
|
2223
2266
|
if (cap > 0 && time - this.lastTime < 1e3 / cap - 1) {
|
|
2267
|
+
this._skippedFrames++;
|
|
2224
2268
|
this.scheduleFrame();
|
|
2225
2269
|
return;
|
|
2226
2270
|
}
|
|
@@ -2231,11 +2275,23 @@ var Scene = (_class2 = class _Scene {
|
|
|
2231
2275
|
}
|
|
2232
2276
|
this.lastTime = time;
|
|
2233
2277
|
if (this.renderMode === "onDemand" && isIdle) {
|
|
2278
|
+
this._skippedFrames++;
|
|
2234
2279
|
this.scheduleFrame();
|
|
2235
2280
|
return;
|
|
2236
2281
|
}
|
|
2237
2282
|
this.dirty = false;
|
|
2283
|
+
const now = typeof performance !== "undefined" ? performance.now() : time;
|
|
2284
|
+
if (this._lastRenderTick > 0) {
|
|
2285
|
+
const interval = time - this._lastRenderTick;
|
|
2286
|
+
if (interval > 0) {
|
|
2287
|
+
this._avgFrameIntervalMs = this._avgFrameIntervalMs === 0 ? interval : this._avgFrameIntervalMs * 0.9 + interval * 0.1;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
this._lastRenderTick = time;
|
|
2291
|
+
this._lastDt = dt;
|
|
2238
2292
|
this.render(this.renderer, dt, time);
|
|
2293
|
+
this._lastFrameMs = (typeof performance !== "undefined" ? performance.now() : time) - now;
|
|
2294
|
+
this._renderedFrames++;
|
|
2239
2295
|
const hasActiveAnimation = this.frameHadAnimation;
|
|
2240
2296
|
const hasInteractive = this.frameHadInteractive;
|
|
2241
2297
|
const wantsContentSync = this.contentProjectionEnabled;
|
|
@@ -2762,15 +2818,15 @@ var TextEntity = (_class3 = class extends _chunk2Z23LTH3js.Entity {
|
|
|
2762
2818
|
|
|
2763
2819
|
|
|
2764
2820
|
|
|
2765
|
-
|
|
2821
|
+
__init70() {this.nodes = []}
|
|
2766
2822
|
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2823
|
+
__init71() {this.fillStyle = "#94a3b8"}
|
|
2824
|
+
__init72() {this.strokeStyle = null}
|
|
2825
|
+
__init73() {this.hoveredFillStyle = "#ffffff"}
|
|
2826
|
+
__init74() {this.lineWidth = 1}
|
|
2827
|
+
__init75() {this.isHovered = false}
|
|
2772
2828
|
constructor(text, atlas, maxWidth, fontSize = 24) {
|
|
2773
|
-
super();_class3.prototype.
|
|
2829
|
+
super();_class3.prototype.__init70.call(this);_class3.prototype.__init71.call(this);_class3.prototype.__init72.call(this);_class3.prototype.__init73.call(this);_class3.prototype.__init74.call(this);_class3.prototype.__init75.call(this);;
|
|
2774
2830
|
this.text = text;
|
|
2775
2831
|
this.atlas = atlas;
|
|
2776
2832
|
this.fontSize = fontSize;
|
|
@@ -2885,15 +2941,15 @@ var TextEntity = (_class3 = class extends _chunk2Z23LTH3js.Entity {
|
|
|
2885
2941
|
// src/components/GridTextEntity.ts
|
|
2886
2942
|
var GridTextEntity = (_class4 = class extends _chunk2Z23LTH3js.Entity {
|
|
2887
2943
|
|
|
2888
|
-
|
|
2889
|
-
|
|
2944
|
+
__init76() {this.fillStyle = "#ffffff"}
|
|
2945
|
+
__init77() {this.grid = []}
|
|
2890
2946
|
// Array of rows
|
|
2891
|
-
|
|
2892
|
-
|
|
2947
|
+
__init78() {this.cols = 0}
|
|
2948
|
+
__init79() {this.rows = 0}
|
|
2893
2949
|
|
|
2894
2950
|
|
|
2895
2951
|
constructor(_atlas, fontSize = 10) {
|
|
2896
|
-
super();_class4.prototype.
|
|
2952
|
+
super();_class4.prototype.__init76.call(this);_class4.prototype.__init77.call(this);_class4.prototype.__init78.call(this);_class4.prototype.__init79.call(this);;
|
|
2897
2953
|
this.fontSize = fontSize;
|
|
2898
2954
|
this.charWidth = fontSize * 1;
|
|
2899
2955
|
this.charHeight = fontSize * 1.1;
|
|
@@ -2985,23 +3041,23 @@ var SplineEntity = (_class5 = class extends _chunk2Z23LTH3js.Entity {
|
|
|
2985
3041
|
|
|
2986
3042
|
|
|
2987
3043
|
|
|
2988
|
-
|
|
2989
|
-
|
|
3044
|
+
__init80() {this.offscreen = null}
|
|
3045
|
+
__init81() {this.baked = false}
|
|
2990
3046
|
/** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
|
|
2991
|
-
|
|
2992
|
-
|
|
3047
|
+
__init82() {this.bakedWidth = 0}
|
|
3048
|
+
__init83() {this.bakedHeight = 0}
|
|
2993
3049
|
/** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
|
|
2994
3050
|
|
|
2995
3051
|
/** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
|
|
2996
|
-
|
|
3052
|
+
__init84() {this.polylines = null}
|
|
2997
3053
|
/**
|
|
2998
3054
|
* When `true`, the renderer draws a rounded-rect outline of the entity's
|
|
2999
3055
|
* local bounds after painting the curves. Useful for drag feedback and
|
|
3000
3056
|
* debugging hit areas. Defaults to `false`.
|
|
3001
3057
|
*/
|
|
3002
|
-
|
|
3058
|
+
__init85() {this.showBounds = false}
|
|
3003
3059
|
constructor(doc, opts = {}) {
|
|
3004
|
-
super();_class5.prototype.
|
|
3060
|
+
super();_class5.prototype.__init80.call(this);_class5.prototype.__init81.call(this);_class5.prototype.__init82.call(this);_class5.prototype.__init83.call(this);_class5.prototype.__init84.call(this);_class5.prototype.__init85.call(this);;
|
|
3005
3061
|
this.doc = doc;
|
|
3006
3062
|
this.lineWidth = _nullishCoalesce(opts.lineWidth, () => ( 2));
|
|
3007
3063
|
this.cache = _nullishCoalesce(opts.cache, () => ( true));
|
|
@@ -3376,9 +3432,9 @@ var Group = class extends _chunk2Z23LTH3js.Entity {
|
|
|
3376
3432
|
// src/math/SpatialHashGrid.ts
|
|
3377
3433
|
var SpatialHashGrid = (_class6 = class {
|
|
3378
3434
|
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
constructor(cellSize = 64) {;_class6.prototype.
|
|
3435
|
+
__init86() {this.grid = /* @__PURE__ */ new Map()}
|
|
3436
|
+
__init87() {this.entityCells = /* @__PURE__ */ new Map()}
|
|
3437
|
+
constructor(cellSize = 64) {;_class6.prototype.__init86.call(this);_class6.prototype.__init87.call(this);
|
|
3382
3438
|
this.cellSize = cellSize;
|
|
3383
3439
|
}
|
|
3384
3440
|
hash(cx, cy) {
|
|
@@ -3470,18 +3526,18 @@ var SpatialHashGrid = (_class6 = class {
|
|
|
3470
3526
|
// src/tree/DOMPortalEntity.ts
|
|
3471
3527
|
var DOMPortalEntity = (_class7 = class extends _chunk2Z23LTH3js.Entity {
|
|
3472
3528
|
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3529
|
+
__init88() {this.isDOMPortal = true}
|
|
3530
|
+
__init89() {this.domListeners = []}
|
|
3531
|
+
__init90() {this.resizeObserver = null}
|
|
3532
|
+
__init91() {this.cachedWidth = 100}
|
|
3533
|
+
__init92() {this.cachedHeight = 100}
|
|
3534
|
+
__init93() {this.lastWidth = ""}
|
|
3535
|
+
__init94() {this.lastHeight = ""}
|
|
3536
|
+
__init95() {this.lastTransform = ""}
|
|
3537
|
+
__init96() {this.lastZIndex = ""}
|
|
3538
|
+
__init97() {this.lastOpacity = ""}
|
|
3483
3539
|
constructor(domElement, width, height, id) {
|
|
3484
|
-
super(id);_class7.prototype.
|
|
3540
|
+
super(id);_class7.prototype.__init88.call(this);_class7.prototype.__init89.call(this);_class7.prototype.__init90.call(this);_class7.prototype.__init91.call(this);_class7.prototype.__init92.call(this);_class7.prototype.__init93.call(this);_class7.prototype.__init94.call(this);_class7.prototype.__init95.call(this);_class7.prototype.__init96.call(this);_class7.prototype.__init97.call(this);;
|
|
3485
3541
|
this.domElement = domElement;
|
|
3486
3542
|
this.width = _nullishCoalesce(width, () => ( 0));
|
|
3487
3543
|
this.height = _nullishCoalesce(height, () => ( 0));
|
package/dist/index.mjs
CHANGED
|
@@ -667,6 +667,19 @@ var Scene = class _Scene {
|
|
|
667
667
|
dirty = true;
|
|
668
668
|
/** Whether to throttle rendering to 2 FPS when the scene is static to save power. */
|
|
669
669
|
autoThrottle = true;
|
|
670
|
+
// --- Frame telemetry (read via `frameStats`) ---------------------------
|
|
671
|
+
/** Wall-clock ms spent inside the last `render()` call. */
|
|
672
|
+
_lastFrameMs = 0;
|
|
673
|
+
/** Rolling exponential average of rendered-frame intervals, in ms. */
|
|
674
|
+
_avgFrameIntervalMs = 0;
|
|
675
|
+
/** dt (ms) handed to the last rendered frame. */
|
|
676
|
+
_lastDt = 0;
|
|
677
|
+
/** Count of frames actually rendered since the loop started. */
|
|
678
|
+
_renderedFrames = 0;
|
|
679
|
+
/** Count of rAF ticks skipped (idle / capped) since the loop started. */
|
|
680
|
+
_skippedFrames = 0;
|
|
681
|
+
/** `time` of the previous *rendered* frame, for interval measurement. */
|
|
682
|
+
_lastRenderTick = 0;
|
|
670
683
|
/**
|
|
671
684
|
* Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
|
|
672
685
|
* the loop renders at most `maxFPS` times per second; animations still run,
|
|
@@ -1318,6 +1331,36 @@ var Scene = class _Scene {
|
|
|
1318
1331
|
markDirty() {
|
|
1319
1332
|
this.dirty = true;
|
|
1320
1333
|
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Live frame telemetry for profilers and devtools overlays. All timings are
|
|
1336
|
+
* measured on the `requestAnimationFrame` loop; a scene driven only by
|
|
1337
|
+
* {@link step} (e.g. deterministic video export) leaves these at their zero
|
|
1338
|
+
* defaults.
|
|
1339
|
+
*
|
|
1340
|
+
* `fps` is derived from the interval between *rendered* frames, so idle
|
|
1341
|
+
* `onDemand` scenes and frames skipped by the {@link maxFPS} cap or the
|
|
1342
|
+
* static auto-throttle do not deflate it — it reports the cadence of actual
|
|
1343
|
+
* redraws, not the raw rAF rate. `frameTimeMs` is the wall-clock cost of the
|
|
1344
|
+
* last `render()` pass alone (excludes a11y/content-projection sync).
|
|
1345
|
+
*
|
|
1346
|
+
* The renderer always repaints the full canvas, so there is no partial
|
|
1347
|
+
* dirty-rectangle to expose; `dirty` is the boolean redraw-pending flag and
|
|
1348
|
+
* `pendingRedraw` reflects whether the next `onDemand` tick will actually
|
|
1349
|
+
* render.
|
|
1350
|
+
*/
|
|
1351
|
+
get frameStats() {
|
|
1352
|
+
const interval = this._avgFrameIntervalMs;
|
|
1353
|
+
return {
|
|
1354
|
+
fps: interval > 0 ? Math.min(1e3 / interval, this.maxFPS > 0 ? this.maxFPS : 1e3 / interval) : 0,
|
|
1355
|
+
frameTimeMs: this._lastFrameMs,
|
|
1356
|
+
frameIntervalMs: interval,
|
|
1357
|
+
dt: this._lastDt,
|
|
1358
|
+
renderedFrames: this._renderedFrames,
|
|
1359
|
+
skippedFrames: this._skippedFrames,
|
|
1360
|
+
renderMode: this.renderMode,
|
|
1361
|
+
dirty: this.dirty
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1321
1364
|
/** True when any node in the subtree has a pending animation. */
|
|
1322
1365
|
/** True when any node in the subtree is interactive (drives a11y sync). */
|
|
1323
1366
|
syncOptionalAttribute(element, name, value) {
|
|
@@ -2221,6 +2264,7 @@ var Scene = class _Scene {
|
|
|
2221
2264
|
cap = Math.min(cap, 2);
|
|
2222
2265
|
}
|
|
2223
2266
|
if (cap > 0 && time - this.lastTime < 1e3 / cap - 1) {
|
|
2267
|
+
this._skippedFrames++;
|
|
2224
2268
|
this.scheduleFrame();
|
|
2225
2269
|
return;
|
|
2226
2270
|
}
|
|
@@ -2231,11 +2275,23 @@ var Scene = class _Scene {
|
|
|
2231
2275
|
}
|
|
2232
2276
|
this.lastTime = time;
|
|
2233
2277
|
if (this.renderMode === "onDemand" && isIdle) {
|
|
2278
|
+
this._skippedFrames++;
|
|
2234
2279
|
this.scheduleFrame();
|
|
2235
2280
|
return;
|
|
2236
2281
|
}
|
|
2237
2282
|
this.dirty = false;
|
|
2283
|
+
const now = typeof performance !== "undefined" ? performance.now() : time;
|
|
2284
|
+
if (this._lastRenderTick > 0) {
|
|
2285
|
+
const interval = time - this._lastRenderTick;
|
|
2286
|
+
if (interval > 0) {
|
|
2287
|
+
this._avgFrameIntervalMs = this._avgFrameIntervalMs === 0 ? interval : this._avgFrameIntervalMs * 0.9 + interval * 0.1;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
this._lastRenderTick = time;
|
|
2291
|
+
this._lastDt = dt;
|
|
2238
2292
|
this.render(this.renderer, dt, time);
|
|
2293
|
+
this._lastFrameMs = (typeof performance !== "undefined" ? performance.now() : time) - now;
|
|
2294
|
+
this._renderedFrames++;
|
|
2239
2295
|
const hasActiveAnimation = this.frameHadAnimation;
|
|
2240
2296
|
const hasInteractive = this.frameHadInteractive;
|
|
2241
2297
|
const wantsContentSync = this.contentProjectionEnabled;
|
package/dist/tree/Scene.d.ts
CHANGED
|
@@ -101,6 +101,28 @@ export interface SceneOptions {
|
|
|
101
101
|
}
|
|
102
102
|
/** Frame-rate the loop is capped to when the OS requests reduced motion. */
|
|
103
103
|
export declare const REDUCED_MOTION_FPS = 30;
|
|
104
|
+
/**
|
|
105
|
+
* Live render-loop telemetry, read from {@link Scene.frameStats}. See that
|
|
106
|
+
* getter for how each field is measured.
|
|
107
|
+
*/
|
|
108
|
+
export interface FrameStats {
|
|
109
|
+
/** Rendered-frame cadence (Hz), clamped to `maxFPS`. `0` before the first pair of rendered frames. */
|
|
110
|
+
fps: number;
|
|
111
|
+
/** Wall-clock ms of the last `render()` pass (excludes a11y/content sync). */
|
|
112
|
+
frameTimeMs: number;
|
|
113
|
+
/** Smoothed interval between rendered frames, in ms (EMA). */
|
|
114
|
+
frameIntervalMs: number;
|
|
115
|
+
/** dt (ms) handed to the last rendered frame. */
|
|
116
|
+
dt: number;
|
|
117
|
+
/** Total frames rendered since `start()`. */
|
|
118
|
+
renderedFrames: number;
|
|
119
|
+
/** Total rAF ticks skipped (idle/onDemand/capped) since `start()`. */
|
|
120
|
+
skippedFrames: number;
|
|
121
|
+
/** The scene's current render mode. */
|
|
122
|
+
renderMode: 'always' | 'onDemand';
|
|
123
|
+
/** Whether a redraw is currently pending (the boolean dirty flag). */
|
|
124
|
+
dirty: boolean;
|
|
125
|
+
}
|
|
104
126
|
export interface A11yTreeNode {
|
|
105
127
|
id: string;
|
|
106
128
|
tag: string;
|
|
@@ -147,6 +169,18 @@ export declare class Scene {
|
|
|
147
169
|
private dirty;
|
|
148
170
|
/** Whether to throttle rendering to 2 FPS when the scene is static to save power. */
|
|
149
171
|
autoThrottle: boolean;
|
|
172
|
+
/** Wall-clock ms spent inside the last `render()` call. */
|
|
173
|
+
private _lastFrameMs;
|
|
174
|
+
/** Rolling exponential average of rendered-frame intervals, in ms. */
|
|
175
|
+
private _avgFrameIntervalMs;
|
|
176
|
+
/** dt (ms) handed to the last rendered frame. */
|
|
177
|
+
private _lastDt;
|
|
178
|
+
/** Count of frames actually rendered since the loop started. */
|
|
179
|
+
private _renderedFrames;
|
|
180
|
+
/** Count of rAF ticks skipped (idle / capped) since the loop started. */
|
|
181
|
+
private _skippedFrames;
|
|
182
|
+
/** `time` of the previous *rendered* frame, for interval measurement. */
|
|
183
|
+
private _lastRenderTick;
|
|
150
184
|
/**
|
|
151
185
|
* Frame-rate cap (power saving). `0` = uncapped (native refresh). When set,
|
|
152
186
|
* the loop renders at most `maxFPS` times per second; animations still run,
|
|
@@ -339,6 +373,24 @@ export declare class Scene {
|
|
|
339
373
|
* entity state outside of {@link Entity.animate} so the change is rendered.
|
|
340
374
|
*/
|
|
341
375
|
markDirty(): void;
|
|
376
|
+
/**
|
|
377
|
+
* Live frame telemetry for profilers and devtools overlays. All timings are
|
|
378
|
+
* measured on the `requestAnimationFrame` loop; a scene driven only by
|
|
379
|
+
* {@link step} (e.g. deterministic video export) leaves these at their zero
|
|
380
|
+
* defaults.
|
|
381
|
+
*
|
|
382
|
+
* `fps` is derived from the interval between *rendered* frames, so idle
|
|
383
|
+
* `onDemand` scenes and frames skipped by the {@link maxFPS} cap or the
|
|
384
|
+
* static auto-throttle do not deflate it — it reports the cadence of actual
|
|
385
|
+
* redraws, not the raw rAF rate. `frameTimeMs` is the wall-clock cost of the
|
|
386
|
+
* last `render()` pass alone (excludes a11y/content-projection sync).
|
|
387
|
+
*
|
|
388
|
+
* The renderer always repaints the full canvas, so there is no partial
|
|
389
|
+
* dirty-rectangle to expose; `dirty` is the boolean redraw-pending flag and
|
|
390
|
+
* `pendingRedraw` reflects whether the next `onDemand` tick will actually
|
|
391
|
+
* render.
|
|
392
|
+
*/
|
|
393
|
+
get frameStats(): FrameStats;
|
|
342
394
|
/** True when any node in the subtree has a pending animation. */
|
|
343
395
|
/** True when any node in the subtree is interactive (drives a11y sync). */
|
|
344
396
|
private syncOptionalAttribute;
|