rendx-engine 0.2.0 → 0.4.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/main.js CHANGED
@@ -403,7 +403,7 @@ getEmitter_fn = function() {
403
403
  };
404
404
 
405
405
  // src/core/graphics.ts
406
- var _nameMap, _classlist, _Graphics_instances, removeFromNameMap_fn, updateMat2d_fn, updateWorldMatrix_fn, updateEZ_fn;
406
+ var _nameMap, _classlist, _Graphics_instances, removeFromNameMap_fn, inheritState_fn, updateMat2d_fn, updateWorldMatrix_fn, updateEZ_fn;
407
407
  var Graphics = class extends EventTarget {
408
408
  constructor() {
409
409
  super(...arguments);
@@ -519,6 +519,7 @@ var Graphics = class extends EventTarget {
519
519
  this.children.push(child);
520
520
  if (__privateGet(this, _nameMap).has(child.name)) throw new Error(`The name "${child.name}" is already used.`);
521
521
  if (child.name) __privateGet(this, _nameMap).set(child.name, child);
522
+ __privateMethod(this, _Graphics_instances, inheritState_fn).call(this, child);
522
523
  this.setDirty(true);
523
524
  return this;
524
525
  }
@@ -527,6 +528,7 @@ var Graphics = class extends EventTarget {
527
528
  child.parent = this;
528
529
  this.children.unshift(child);
529
530
  __privateMethod(this, _Graphics_instances, removeFromNameMap_fn).call(this, child);
531
+ __privateMethod(this, _Graphics_instances, inheritState_fn).call(this, child);
530
532
  this.setDirty(true);
531
533
  return this;
532
534
  }
@@ -769,6 +771,22 @@ _Graphics_instances = new WeakSet();
769
771
  removeFromNameMap_fn = function(g) {
770
772
  if (__privateGet(this, _nameMap).has(g.name)) __privateGet(this, _nameMap).delete(g.name);
771
773
  };
774
+ /**
775
+ * 将父节点的可继承状态(visible / display / pointerEvents)传播给新增子节点。
776
+ * 模拟 DOM 中 CSS 继承属性的行为:
777
+ * - 父级 pointer-events: none → 子级默认继承 none,除非子级显式覆盖
778
+ * - 父级 visibility: hidden → 同上
779
+ * - 父级 display: none → 整棵子树不渲染
780
+ *
781
+ * 使用 bySelf=false 调用,尊重子节点的 autoX 标记:
782
+ * - autoX=true(默认)→ 接受父级值
783
+ * - autoX=false(子节点显式设置过)→ 保持自身值不变
784
+ */
785
+ inheritState_fn = function(child) {
786
+ if (!this.visible) child.setVisible(false, false);
787
+ if (!this.display) child.setDisplay(false, false);
788
+ if (!this.pointerEvents) child.setPointerEvents(false, false);
789
+ };
772
790
  updateMat2d_fn = function() {
773
791
  if (!this.needUpdate) return;
774
792
  mat2d.identity(this.matrix);
@@ -1178,7 +1196,7 @@ var Group = class extends Graphics {
1178
1196
 
1179
1197
  // src/scene/node.ts
1180
1198
  import { mat2d as mat2d3, vec2 as vec23 } from "gl-matrix";
1181
- import { BoundingBox as BoundingBox15 } from "rendx-bounding";
1199
+ import { BoundingBox as BoundingBox16 } from "rendx-bounding";
1182
1200
 
1183
1201
  // src/shapes/symbol.ts
1184
1202
  import { BoundingBox as BoundingBox2 } from "rendx-bounding";
@@ -1575,6 +1593,8 @@ var ArcShape = class extends Shape {
1575
1593
  };
1576
1594
 
1577
1595
  // src/shapes/path.ts
1596
+ import { BoundingBox as BoundingBox9 } from "rendx-bounding";
1597
+ import { pathBBox } from "rendx-path";
1578
1598
  var PathShape = class extends Shape {
1579
1599
  constructor() {
1580
1600
  super(...arguments);
@@ -1583,11 +1603,19 @@ var PathShape = class extends Shape {
1583
1603
  from(d) {
1584
1604
  this.d = d;
1585
1605
  this.p = null;
1606
+ if (this.autoNeedUpdate) this.needUpdate = true;
1607
+ }
1608
+ box() {
1609
+ const result = pathBBox(this.d);
1610
+ if (result) {
1611
+ this.boundingBox = BoundingBox9.fromPoints(result[0], result[1], result[2], result[3]);
1612
+ }
1613
+ return this.boundingBox;
1586
1614
  }
1587
1615
  };
1588
1616
 
1589
1617
  // src/shapes/text.ts
1590
- import { BoundingBox as BoundingBox9 } from "rendx-bounding";
1618
+ import { BoundingBox as BoundingBox10 } from "rendx-bounding";
1591
1619
  var _attrs;
1592
1620
  var _TextShape = class _TextShape extends Shape {
1593
1621
  constructor() {
@@ -1620,7 +1648,7 @@ var _TextShape = class _TextShape extends Shape {
1620
1648
  if (measureFn && this.text) {
1621
1649
  const bb = measureFn(this.text, __privateGet(this, _attrs));
1622
1650
  if (bb) {
1623
- this.boundingBox = BoundingBox9.fromRect(this.x, this.y - bb.height, bb.width, bb.height);
1651
+ this.boundingBox = BoundingBox10.fromRect(this.x, this.y - bb.height, bb.width, bb.height);
1624
1652
  return this.boundingBox;
1625
1653
  }
1626
1654
  }
@@ -1651,7 +1679,7 @@ _TextShape.defaultMeasure = null;
1651
1679
  var TextShape = _TextShape;
1652
1680
 
1653
1681
  // src/shapes/line.ts
1654
- import { BoundingBox as BoundingBox10 } from "rendx-bounding";
1682
+ import { BoundingBox as BoundingBox11 } from "rendx-bounding";
1655
1683
  var LineShape = class extends Shape {
1656
1684
  constructor() {
1657
1685
  super(...arguments);
@@ -1683,13 +1711,13 @@ var LineShape = class extends Shape {
1683
1711
  }
1684
1712
  box() {
1685
1713
  const { x1, y1, x2, y2 } = this;
1686
- this.boundingBox = BoundingBox10.fromPoints(x1, y1, x2, y2);
1714
+ this.boundingBox = BoundingBox11.fromPoints(x1, y1, x2, y2);
1687
1715
  return this.boundingBox;
1688
1716
  }
1689
1717
  };
1690
1718
 
1691
1719
  // src/shapes/rect.ts
1692
- import { BoundingBox as BoundingBox11 } from "rendx-bounding";
1720
+ import { BoundingBox as BoundingBox12 } from "rendx-bounding";
1693
1721
  var RectShape = class extends Shape {
1694
1722
  constructor() {
1695
1723
  super(...arguments);
@@ -1719,13 +1747,13 @@ var RectShape = class extends Shape {
1719
1747
  }
1720
1748
  box() {
1721
1749
  const { x, y, width, height } = this;
1722
- this.boundingBox = BoundingBox11.fromRect(x, y, width, height);
1750
+ this.boundingBox = BoundingBox12.fromRect(x, y, width, height);
1723
1751
  return this.boundingBox;
1724
1752
  }
1725
1753
  };
1726
1754
 
1727
1755
  // src/shapes/circle.ts
1728
- import { BoundingBox as BoundingBox12 } from "rendx-bounding";
1756
+ import { BoundingBox as BoundingBox13 } from "rendx-bounding";
1729
1757
  var CircleShape = class extends Shape {
1730
1758
  constructor() {
1731
1759
  super(...arguments);
@@ -1752,13 +1780,13 @@ var CircleShape = class extends Shape {
1752
1780
  }
1753
1781
  box() {
1754
1782
  const { cx, cy, r } = this;
1755
- this.boundingBox = BoundingBox12.fromPoints(cx - r, cy - r, cx + r, cy + r);
1783
+ this.boundingBox = BoundingBox13.fromPoints(cx - r, cy - r, cx + r, cy + r);
1756
1784
  return this.boundingBox;
1757
1785
  }
1758
1786
  };
1759
1787
 
1760
1788
  // src/shapes/image.ts
1761
- import { BoundingBox as BoundingBox13 } from "rendx-bounding";
1789
+ import { BoundingBox as BoundingBox14 } from "rendx-bounding";
1762
1790
  var ImageShape = class extends Shape {
1763
1791
  constructor() {
1764
1792
  super(...arguments);
@@ -1802,13 +1830,13 @@ var ImageShape = class extends Shape {
1802
1830
  }
1803
1831
  box() {
1804
1832
  const { x, y, width, height } = this;
1805
- this.boundingBox = BoundingBox13.fromRect(x, y, width, height);
1833
+ this.boundingBox = BoundingBox14.fromRect(x, y, width, height);
1806
1834
  return this.boundingBox;
1807
1835
  }
1808
1836
  };
1809
1837
 
1810
1838
  // src/shapes/rect-buffer.ts
1811
- import { BoundingBox as BoundingBox14 } from "rendx-bounding";
1839
+ import { BoundingBox as BoundingBox15 } from "rendx-bounding";
1812
1840
  import { createShape as createShape7 } from "rendx-shape";
1813
1841
  import { Path as Path9 } from "rendx-path";
1814
1842
  var RectBufferShape = class extends Shape {
@@ -1874,7 +1902,7 @@ var RectBufferShape = class extends Shape {
1874
1902
  x1 = Math.max(x1, x + width);
1875
1903
  y1 = Math.max(y1, y + height);
1876
1904
  }
1877
- this.boundingBox = BoundingBox14.fromPoints(x0, y0, x1, y1);
1905
+ this.boundingBox = BoundingBox15.fromPoints(x0, y0, x1, y1);
1878
1906
  return this.boundingBox;
1879
1907
  }
1880
1908
  };
@@ -1977,7 +2005,7 @@ var _Node = class _Node extends Graphics {
1977
2005
  if (wx > maxX) maxX = wx;
1978
2006
  if (wy < minY) minY = wy;
1979
2007
  if (wy > maxY) maxY = wy;
1980
- return BoundingBox15.fromPoints(minX, minY, maxX, maxY);
2008
+ return BoundingBox16.fromPoints(minX, minY, maxX, maxY);
1981
2009
  }
1982
2010
  /**
1983
2011
  * 命中检测:将屏幕坐标通过逆世界矩阵变换为本地坐标,然后检测是否命中
@@ -2487,6 +2515,48 @@ createListener_fn = function(type) {
2487
2515
  };
2488
2516
  };
2489
2517
 
2518
+ // src/app.ts
2519
+ import EventEmitter2 from "eventemitter3";
2520
+
2521
+ // src/scheduler.ts
2522
+ var _pendingState, _microTaskQueued, _onFlush;
2523
+ var Scheduler = class {
2524
+ constructor(onFlush) {
2525
+ __privateAdd(this, _pendingState, /* @__PURE__ */ new Set());
2526
+ __privateAdd(this, _microTaskQueued, false);
2527
+ __privateAdd(this, _onFlush);
2528
+ __privateSet(this, _onFlush, onFlush);
2529
+ }
2530
+ /**
2531
+ * 标记 state key 变更,批量到微任务统一通知。
2532
+ * 同一轮微任务内多次调用同一 key 只通知一次。
2533
+ */
2534
+ markState(key) {
2535
+ __privateGet(this, _pendingState).add(key);
2536
+ if (!__privateGet(this, _microTaskQueued)) {
2537
+ __privateSet(this, _microTaskQueued, true);
2538
+ queueMicrotask(() => {
2539
+ __privateSet(this, _microTaskQueued, false);
2540
+ const keys = new Set(__privateGet(this, _pendingState));
2541
+ __privateGet(this, _pendingState).clear();
2542
+ __privateGet(this, _onFlush).call(this, keys);
2543
+ });
2544
+ }
2545
+ }
2546
+ /** 是否有待通知的 state 变更 */
2547
+ get hasPending() {
2548
+ return __privateGet(this, _pendingState).size > 0;
2549
+ }
2550
+ /** 清空待通知队列(用于 dispose) */
2551
+ clear() {
2552
+ __privateGet(this, _pendingState).clear();
2553
+ __privateSet(this, _microTaskQueued, false);
2554
+ }
2555
+ };
2556
+ _pendingState = new WeakMap();
2557
+ _microTaskQueued = new WeakMap();
2558
+ _onFlush = new WeakMap();
2559
+
2490
2560
  // src/serialization.ts
2491
2561
  var SHAPE_FROM_KEYS = {
2492
2562
  text: ["text", "x", "y"],
@@ -2648,21 +2718,39 @@ function deserialize(json, cfg) {
2648
2718
  }
2649
2719
 
2650
2720
  // src/app.ts
2651
- var _rafId, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _App_instances, tick_fn;
2721
+ var _rafId, _renderDirty, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _state, _stateMeta, _scheduler, _App_instances, resolveIndex_fn, nextAvailableIndex_fn, frame_fn;
2652
2722
  var _App = class _App {
2653
2723
  /**
2654
2724
  * @param cfg - 引擎配置(width/height/layers/autoResize 等)
2655
2725
  */
2656
2726
  constructor(cfg = {}) {
2657
2727
  __privateAdd(this, _App_instances);
2728
+ /**
2729
+ * 中心化事件总线 — 纯信号(不传数据)。
2730
+ * 插件通过 `app.bus.emit('eventName')` 发布信号,
2731
+ * 消费方通过 `app.bus.on('eventName', handler)` 订阅。
2732
+ */
2733
+ this.bus = new EventEmitter2();
2658
2734
  __privateAdd(this, _rafId, null);
2735
+ __privateAdd(this, _renderDirty, false);
2659
2736
  __privateAdd(this, _mounted, false);
2660
2737
  __privateAdd(this, _container, null);
2661
2738
  __privateAdd(this, _eventLayer);
2662
2739
  __privateAdd(this, _resizeObserver, null);
2663
2740
  __privateAdd(this, _plugins, []);
2741
+ // ========================
2742
+ // State Management
2743
+ // ========================
2744
+ __privateAdd(this, _state, /* @__PURE__ */ new Map());
2745
+ __privateAdd(this, _stateMeta, /* @__PURE__ */ new Map());
2746
+ __privateAdd(this, _scheduler);
2664
2747
  this.cfg = cfg;
2665
2748
  this.scene = new Scene();
2749
+ __privateSet(this, _scheduler, new Scheduler((keys) => {
2750
+ for (const key of keys) {
2751
+ this.bus.emit(`state:${key}`);
2752
+ }
2753
+ }));
2666
2754
  __privateSet(this, _eventLayer, new Layer("__event__", 99999, this.cfg, true));
2667
2755
  this.scene.registerLayer(__privateGet(this, _eventLayer));
2668
2756
  this.scene.registerLayer(new Layer("default", 0, this.cfg));
@@ -2735,7 +2823,30 @@ var _App = class _App {
2735
2823
  return this.scene.getLayer(name);
2736
2824
  }
2737
2825
  /**
2738
- * 注册插件(同名插件不会重复注册)
2826
+ * 获取或创建图层(get-or-create 语义)。
2827
+ * 多个插件声明同名图层时,只会创建一次。
2828
+ *
2829
+ * zIndex 语义:
2830
+ * - 传入时代表全局层级意图(负数 = default 之下,正数 = default 之上)
2831
+ * - 若与已有层冲突,自动向上偏移到最近可用值
2832
+ * - 省略时自动分配(当前最高非事件层 + 1)
2833
+ *
2834
+ * @param name - 层名称
2835
+ * @param zIndex - 全局层级意图(可选)
2836
+ */
2837
+ acquireLayer(name, zIndex) {
2838
+ const existing = this.scene.getLayer(name);
2839
+ if (existing) return existing;
2840
+ const index = zIndex !== void 0 ? __privateMethod(this, _App_instances, resolveIndex_fn).call(this, zIndex) : __privateMethod(this, _App_instances, nextAvailableIndex_fn).call(this);
2841
+ return this.addLayer(name, index);
2842
+ }
2843
+ /**
2844
+ * 注册插件。流程:
2845
+ * 1. 去重检查(同名跳过)
2846
+ * 2. 注册 state 声明(key 冲突则抛错)
2847
+ * 3. 自动 acquireLayer(声明的图层)
2848
+ * 4. 调用 plugin.install(app)
2849
+ *
2739
2850
  * @param plugin - 实现 Plugin 接口的插件实例
2740
2851
  */
2741
2852
  use(plugin) {
@@ -2743,6 +2854,22 @@ var _App = class _App {
2743
2854
  console.warn(`Plugin "${plugin.name}" is already registered.`);
2744
2855
  return this;
2745
2856
  }
2857
+ if (plugin.state) {
2858
+ for (const decl of plugin.state) {
2859
+ if (__privateGet(this, _stateMeta).has(decl.key)) {
2860
+ const owner = __privateGet(this, _stateMeta).get(decl.key).owner;
2861
+ throw new Error(`State key "${decl.key}" already declared by plugin "${owner}". Plugin "${plugin.name}" cannot redeclare it.`);
2862
+ }
2863
+ __privateGet(this, _stateMeta).set(decl.key, { owner: plugin.name, description: decl.description });
2864
+ __privateGet(this, _state).set(decl.key, decl.initial);
2865
+ }
2866
+ }
2867
+ if (plugin.layers) {
2868
+ const sorted = [...plugin.layers].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
2869
+ for (const decl of sorted) {
2870
+ this.acquireLayer(decl.name, decl.zIndex);
2871
+ }
2872
+ }
2746
2873
  __privateGet(this, _plugins).push(plugin);
2747
2874
  plugin.install(this);
2748
2875
  return this;
@@ -2751,6 +2878,40 @@ var _App = class _App {
2751
2878
  getPlugin(name) {
2752
2879
  return __privateGet(this, _plugins).find((p) => p.name === name);
2753
2880
  }
2881
+ // ========================
2882
+ // Centralized State
2883
+ // ========================
2884
+ /**
2885
+ * 写入 state(同步写入 + 异步批量通知)。
2886
+ * key 必须先由某个插件在 `state[]` 中声明,否则抛错。
2887
+ * 写入后立即可通过 `getState()` 读到最新值,通知在微任务中批量发出。
2888
+ */
2889
+ setState(key, value) {
2890
+ if (!__privateGet(this, _stateMeta).has(key)) {
2891
+ throw new Error(`State key "${key}" is not declared by any plugin. Plugins must declare state keys in their "state" array.`);
2892
+ }
2893
+ __privateGet(this, _state).set(key, value);
2894
+ __privateGet(this, _scheduler).markState(key);
2895
+ }
2896
+ /**
2897
+ * 读取 state(同步读取,始终返回最新值)。
2898
+ * @returns 对应 key 的当前值,不存在则返回 undefined
2899
+ */
2900
+ getState(key) {
2901
+ return __privateGet(this, _state).get(key);
2902
+ }
2903
+ /**
2904
+ * 导出完整 state 快照(调试/devtools 用)。
2905
+ * 返回每个 key 的当前值、所属插件和描述信息。
2906
+ */
2907
+ dumpState() {
2908
+ const result = {};
2909
+ for (const [key, value] of __privateGet(this, _state)) {
2910
+ const meta = __privateGet(this, _stateMeta).get(key);
2911
+ result[key] = { value, owner: meta.owner, description: meta.description };
2912
+ }
2913
+ return result;
2914
+ }
2754
2915
  /** 同步渲染一帧。适用于静态内容,仅重绘脏层 */
2755
2916
  render() {
2756
2917
  for (const layer of this.scene.layers) {
@@ -2759,10 +2920,12 @@ var _App = class _App {
2759
2920
  }
2760
2921
  }
2761
2922
  }
2762
- /** 请求异步渲染循环(rAF),有动画时自动继续,无变化时停止 */
2923
+ /** 请求异步渲染帧(幂等,一帧只执行一次)。无变化时自动停止 rAF */
2763
2924
  requestRender() {
2764
- if (__privateGet(this, _rafId) !== null) return;
2765
- __privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, tick_fn).call(this, t)));
2925
+ __privateSet(this, _renderDirty, true);
2926
+ if (__privateGet(this, _rafId) === null) {
2927
+ __privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
2928
+ }
2766
2929
  }
2767
2930
  /**
2768
2931
  * 调整画布尺寸,同步更新所有层、容器和视口矩阵
@@ -2788,6 +2951,26 @@ var _App = class _App {
2788
2951
  plugin.resize?.(width, height);
2789
2952
  }
2790
2953
  }
2954
+ // ========================
2955
+ // Cursor
2956
+ // ========================
2957
+ /**
2958
+ * 设置容器的鼠标光标样式。
2959
+ * @param cursor - CSS cursor 值(如 'pointer'、'crosshair'、'grab')
2960
+ */
2961
+ setCursor(cursor) {
2962
+ if (__privateGet(this, _container)) {
2963
+ __privateGet(this, _container).style.cursor = cursor;
2964
+ }
2965
+ }
2966
+ /**
2967
+ * 重置鼠标光标为默认值。
2968
+ */
2969
+ resetCursor() {
2970
+ if (__privateGet(this, _container)) {
2971
+ __privateGet(this, _container).style.cursor = "";
2972
+ }
2973
+ }
2791
2974
  clear() {
2792
2975
  if (__privateGet(this, _rafId) !== null) {
2793
2976
  cancelAnimationFrame(__privateGet(this, _rafId));
@@ -2803,6 +2986,10 @@ var _App = class _App {
2803
2986
  plugin.dispose?.();
2804
2987
  }
2805
2988
  __privateSet(this, _plugins, []);
2989
+ this.bus.removeAllListeners();
2990
+ __privateGet(this, _state).clear();
2991
+ __privateGet(this, _stateMeta).clear();
2992
+ __privateGet(this, _scheduler).clear();
2806
2993
  if (__privateGet(this, _resizeObserver)) {
2807
2994
  __privateGet(this, _resizeObserver).disconnect();
2808
2995
  __privateSet(this, _resizeObserver, null);
@@ -2837,7 +3024,17 @@ var _App = class _App {
2837
3024
  }
2838
3025
  /** 序列化所有渲染层的场景图为 JSON,可用于保存/回放 */
2839
3026
  toJSON() {
2840
- return serialize(this.scene.layers, this.cfg.width ?? 800, this.cfg.height ?? 600);
3027
+ const json = serialize(this.scene.layers, this.cfg.width ?? 800, this.cfg.height ?? 600);
3028
+ const pluginsData = {};
3029
+ for (const plugin of __privateGet(this, _plugins)) {
3030
+ if (plugin.serialize) {
3031
+ pluginsData[plugin.name] = plugin.serialize();
3032
+ }
3033
+ }
3034
+ if (Object.keys(pluginsData).length > 0) {
3035
+ json.plugins = pluginsData;
3036
+ }
3037
+ return json;
2841
3038
  }
2842
3039
  /**
2843
3040
  * 从 JSON 快照创建新的 App 实例(静态工厂方法)
@@ -2876,17 +3073,49 @@ var _App = class _App {
2876
3073
  __privateGet(this, _container).insertBefore(layer.renderer.el, __privateGet(this, _eventLayer).renderer.el);
2877
3074
  }
2878
3075
  }
3076
+ if (json.plugins) {
3077
+ for (const plugin of __privateGet(this, _plugins)) {
3078
+ const data = json.plugins[plugin.name];
3079
+ if (data && plugin.deserialize) {
3080
+ plugin.deserialize(data);
3081
+ }
3082
+ }
3083
+ }
2879
3084
  }
2880
3085
  };
2881
3086
  _rafId = new WeakMap();
3087
+ _renderDirty = new WeakMap();
2882
3088
  _mounted = new WeakMap();
2883
3089
  _container = new WeakMap();
2884
3090
  _eventLayer = new WeakMap();
2885
3091
  _resizeObserver = new WeakMap();
2886
3092
  _plugins = new WeakMap();
3093
+ _state = new WeakMap();
3094
+ _stateMeta = new WeakMap();
3095
+ _scheduler = new WeakMap();
2887
3096
  _App_instances = new WeakSet();
2888
- tick_fn = function(time) {
2889
- __privateSet(this, _rafId, null);
3097
+ /**
3098
+ * 解析 zIndex:若已被占用则向上偏移到最近可用值。
3099
+ * 保证返回值不会与任何已有层的 layerIndex 冲突。
3100
+ */
3101
+ resolveIndex_fn = function(desired) {
3102
+ const used = new Set(this.scene.layers.map((l) => l.layerIndex));
3103
+ while (used.has(desired)) desired++;
3104
+ return desired;
3105
+ };
3106
+ /**
3107
+ * 自动分配:当前最高非事件层 index + 1。
3108
+ * 保证返回值始终 >= 1(default 层固定为 0)。
3109
+ */
3110
+ nextAvailableIndex_fn = function() {
3111
+ let max = 0;
3112
+ for (const l of this.scene.layers) {
3113
+ if (!l.isEventLayer && l.layerIndex > max) max = l.layerIndex;
3114
+ }
3115
+ return max + 1;
3116
+ };
3117
+ frame_fn = function(time) {
3118
+ __privateSet(this, _renderDirty, false);
2890
3119
  this.scene.tick(time);
2891
3120
  let anyDirty = false;
2892
3121
  for (const layer of this.scene.layers) {
@@ -2895,8 +3124,10 @@ tick_fn = function(time) {
2895
3124
  anyDirty = true;
2896
3125
  }
2897
3126
  }
2898
- if (anyDirty) {
2899
- this.requestRender();
3127
+ if (anyDirty || __privateGet(this, _renderDirty)) {
3128
+ __privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
3129
+ } else {
3130
+ __privateSet(this, _rafId, null);
2900
3131
  }
2901
3132
  };
2902
3133
  var App = _App;
@@ -2927,6 +3158,7 @@ export {
2927
3158
  Renderer,
2928
3159
  RoundShape,
2929
3160
  Scene,
3161
+ Scheduler,
2930
3162
  SectorShape,
2931
3163
  SectorTransform,
2932
3164
  Shape,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rendx-engine",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "2D scene graph engine with animation, events, and plugin system",
5
5
  "license": "MIT",
6
6
  "author": "wei.liang (https://github.com/weiliang0121)",
@@ -32,14 +32,14 @@
32
32
  "dependencies": {
33
33
  "eventemitter3": "^5.0.4",
34
34
  "gl-matrix": "^3.4.4",
35
- "rendx-bounding": "^0.1.0",
36
- "rendx-canvas": "^0.1.0",
37
- "rendx-ease": "^0.1.0",
38
- "rendx-interpolate": "^0.1.0",
39
- "rendx-path": "^0.1.0",
40
- "rendx-shape": "^0.1.0",
41
- "rendx-svg": "^0.1.0",
42
- "rendx-core": "^0.1.0"
35
+ "rendx-bounding": "^0.1.1",
36
+ "rendx-canvas": "^0.1.1",
37
+ "rendx-ease": "^0.1.1",
38
+ "rendx-interpolate": "^0.1.1",
39
+ "rendx-path": "^0.2.0",
40
+ "rendx-shape": "^0.1.2",
41
+ "rendx-svg": "^0.1.1",
42
+ "rendx-core": "^0.1.1"
43
43
  },
44
44
  "sideEffects": false,
45
45
  "files": [