rendx-engine 0.2.0 → 0.3.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.cjs +195 -10
- package/dist/main.d.cts +112 -4
- package/dist/main.d.ts +112 -4
- package/dist/main.js +194 -10
- package/package.json +9 -9
package/dist/main.cjs
CHANGED
|
@@ -64,6 +64,7 @@ __export(main_exports, {
|
|
|
64
64
|
Renderer: () => Renderer,
|
|
65
65
|
RoundShape: () => RoundShape,
|
|
66
66
|
Scene: () => Scene,
|
|
67
|
+
Scheduler: () => Scheduler,
|
|
67
68
|
SectorShape: () => SectorShape,
|
|
68
69
|
SectorTransform: () => SectorTransform,
|
|
69
70
|
Shape: () => Shape,
|
|
@@ -2560,6 +2561,48 @@ createListener_fn = function(type) {
|
|
|
2560
2561
|
};
|
|
2561
2562
|
};
|
|
2562
2563
|
|
|
2564
|
+
// src/app.ts
|
|
2565
|
+
var import_eventemitter32 = __toESM(require("eventemitter3"), 1);
|
|
2566
|
+
|
|
2567
|
+
// src/scheduler.ts
|
|
2568
|
+
var _pendingState, _microTaskQueued, _onFlush;
|
|
2569
|
+
var Scheduler = class {
|
|
2570
|
+
constructor(onFlush) {
|
|
2571
|
+
__privateAdd(this, _pendingState, /* @__PURE__ */ new Set());
|
|
2572
|
+
__privateAdd(this, _microTaskQueued, false);
|
|
2573
|
+
__privateAdd(this, _onFlush);
|
|
2574
|
+
__privateSet(this, _onFlush, onFlush);
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* 标记 state key 变更,批量到微任务统一通知。
|
|
2578
|
+
* 同一轮微任务内多次调用同一 key 只通知一次。
|
|
2579
|
+
*/
|
|
2580
|
+
markState(key) {
|
|
2581
|
+
__privateGet(this, _pendingState).add(key);
|
|
2582
|
+
if (!__privateGet(this, _microTaskQueued)) {
|
|
2583
|
+
__privateSet(this, _microTaskQueued, true);
|
|
2584
|
+
queueMicrotask(() => {
|
|
2585
|
+
__privateSet(this, _microTaskQueued, false);
|
|
2586
|
+
const keys = new Set(__privateGet(this, _pendingState));
|
|
2587
|
+
__privateGet(this, _pendingState).clear();
|
|
2588
|
+
__privateGet(this, _onFlush).call(this, keys);
|
|
2589
|
+
});
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
/** 是否有待通知的 state 变更 */
|
|
2593
|
+
get hasPending() {
|
|
2594
|
+
return __privateGet(this, _pendingState).size > 0;
|
|
2595
|
+
}
|
|
2596
|
+
/** 清空待通知队列(用于 dispose) */
|
|
2597
|
+
clear() {
|
|
2598
|
+
__privateGet(this, _pendingState).clear();
|
|
2599
|
+
__privateSet(this, _microTaskQueued, false);
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
_pendingState = new WeakMap();
|
|
2603
|
+
_microTaskQueued = new WeakMap();
|
|
2604
|
+
_onFlush = new WeakMap();
|
|
2605
|
+
|
|
2563
2606
|
// src/serialization.ts
|
|
2564
2607
|
var SHAPE_FROM_KEYS = {
|
|
2565
2608
|
text: ["text", "x", "y"],
|
|
@@ -2721,21 +2764,39 @@ function deserialize(json, cfg) {
|
|
|
2721
2764
|
}
|
|
2722
2765
|
|
|
2723
2766
|
// src/app.ts
|
|
2724
|
-
var _rafId, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _App_instances,
|
|
2767
|
+
var _rafId, _renderDirty, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _state, _stateMeta, _scheduler, _App_instances, resolveIndex_fn, nextAvailableIndex_fn, frame_fn;
|
|
2725
2768
|
var _App = class _App {
|
|
2726
2769
|
/**
|
|
2727
2770
|
* @param cfg - 引擎配置(width/height/layers/autoResize 等)
|
|
2728
2771
|
*/
|
|
2729
2772
|
constructor(cfg = {}) {
|
|
2730
2773
|
__privateAdd(this, _App_instances);
|
|
2774
|
+
/**
|
|
2775
|
+
* 中心化事件总线 — 纯信号(不传数据)。
|
|
2776
|
+
* 插件通过 `app.bus.emit('eventName')` 发布信号,
|
|
2777
|
+
* 消费方通过 `app.bus.on('eventName', handler)` 订阅。
|
|
2778
|
+
*/
|
|
2779
|
+
this.bus = new import_eventemitter32.default();
|
|
2731
2780
|
__privateAdd(this, _rafId, null);
|
|
2781
|
+
__privateAdd(this, _renderDirty, false);
|
|
2732
2782
|
__privateAdd(this, _mounted, false);
|
|
2733
2783
|
__privateAdd(this, _container, null);
|
|
2734
2784
|
__privateAdd(this, _eventLayer);
|
|
2735
2785
|
__privateAdd(this, _resizeObserver, null);
|
|
2736
2786
|
__privateAdd(this, _plugins, []);
|
|
2787
|
+
// ========================
|
|
2788
|
+
// State Management
|
|
2789
|
+
// ========================
|
|
2790
|
+
__privateAdd(this, _state, /* @__PURE__ */ new Map());
|
|
2791
|
+
__privateAdd(this, _stateMeta, /* @__PURE__ */ new Map());
|
|
2792
|
+
__privateAdd(this, _scheduler);
|
|
2737
2793
|
this.cfg = cfg;
|
|
2738
2794
|
this.scene = new Scene();
|
|
2795
|
+
__privateSet(this, _scheduler, new Scheduler((keys) => {
|
|
2796
|
+
for (const key of keys) {
|
|
2797
|
+
this.bus.emit(`state:${key}`);
|
|
2798
|
+
}
|
|
2799
|
+
}));
|
|
2739
2800
|
__privateSet(this, _eventLayer, new Layer("__event__", 99999, this.cfg, true));
|
|
2740
2801
|
this.scene.registerLayer(__privateGet(this, _eventLayer));
|
|
2741
2802
|
this.scene.registerLayer(new Layer("default", 0, this.cfg));
|
|
@@ -2808,7 +2869,30 @@ var _App = class _App {
|
|
|
2808
2869
|
return this.scene.getLayer(name);
|
|
2809
2870
|
}
|
|
2810
2871
|
/**
|
|
2811
|
-
*
|
|
2872
|
+
* 获取或创建图层(get-or-create 语义)。
|
|
2873
|
+
* 多个插件声明同名图层时,只会创建一次。
|
|
2874
|
+
*
|
|
2875
|
+
* zIndex 语义:
|
|
2876
|
+
* - 传入时代表全局层级意图(负数 = default 之下,正数 = default 之上)
|
|
2877
|
+
* - 若与已有层冲突,自动向上偏移到最近可用值
|
|
2878
|
+
* - 省略时自动分配(当前最高非事件层 + 1)
|
|
2879
|
+
*
|
|
2880
|
+
* @param name - 层名称
|
|
2881
|
+
* @param zIndex - 全局层级意图(可选)
|
|
2882
|
+
*/
|
|
2883
|
+
acquireLayer(name, zIndex) {
|
|
2884
|
+
const existing = this.scene.getLayer(name);
|
|
2885
|
+
if (existing) return existing;
|
|
2886
|
+
const index = zIndex !== void 0 ? __privateMethod(this, _App_instances, resolveIndex_fn).call(this, zIndex) : __privateMethod(this, _App_instances, nextAvailableIndex_fn).call(this);
|
|
2887
|
+
return this.addLayer(name, index);
|
|
2888
|
+
}
|
|
2889
|
+
/**
|
|
2890
|
+
* 注册插件。流程:
|
|
2891
|
+
* 1. 去重检查(同名跳过)
|
|
2892
|
+
* 2. 注册 state 声明(key 冲突则抛错)
|
|
2893
|
+
* 3. 自动 acquireLayer(声明的图层)
|
|
2894
|
+
* 4. 调用 plugin.install(app)
|
|
2895
|
+
*
|
|
2812
2896
|
* @param plugin - 实现 Plugin 接口的插件实例
|
|
2813
2897
|
*/
|
|
2814
2898
|
use(plugin) {
|
|
@@ -2816,6 +2900,22 @@ var _App = class _App {
|
|
|
2816
2900
|
console.warn(`Plugin "${plugin.name}" is already registered.`);
|
|
2817
2901
|
return this;
|
|
2818
2902
|
}
|
|
2903
|
+
if (plugin.state) {
|
|
2904
|
+
for (const decl of plugin.state) {
|
|
2905
|
+
if (__privateGet(this, _stateMeta).has(decl.key)) {
|
|
2906
|
+
const owner = __privateGet(this, _stateMeta).get(decl.key).owner;
|
|
2907
|
+
throw new Error(`State key "${decl.key}" already declared by plugin "${owner}". Plugin "${plugin.name}" cannot redeclare it.`);
|
|
2908
|
+
}
|
|
2909
|
+
__privateGet(this, _stateMeta).set(decl.key, { owner: plugin.name, description: decl.description });
|
|
2910
|
+
__privateGet(this, _state).set(decl.key, decl.initial);
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
if (plugin.layers) {
|
|
2914
|
+
const sorted = [...plugin.layers].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
2915
|
+
for (const decl of sorted) {
|
|
2916
|
+
this.acquireLayer(decl.name, decl.zIndex);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2819
2919
|
__privateGet(this, _plugins).push(plugin);
|
|
2820
2920
|
plugin.install(this);
|
|
2821
2921
|
return this;
|
|
@@ -2824,6 +2924,40 @@ var _App = class _App {
|
|
|
2824
2924
|
getPlugin(name) {
|
|
2825
2925
|
return __privateGet(this, _plugins).find((p) => p.name === name);
|
|
2826
2926
|
}
|
|
2927
|
+
// ========================
|
|
2928
|
+
// Centralized State
|
|
2929
|
+
// ========================
|
|
2930
|
+
/**
|
|
2931
|
+
* 写入 state(同步写入 + 异步批量通知)。
|
|
2932
|
+
* key 必须先由某个插件在 `state[]` 中声明,否则抛错。
|
|
2933
|
+
* 写入后立即可通过 `getState()` 读到最新值,通知在微任务中批量发出。
|
|
2934
|
+
*/
|
|
2935
|
+
setState(key, value) {
|
|
2936
|
+
if (!__privateGet(this, _stateMeta).has(key)) {
|
|
2937
|
+
throw new Error(`State key "${key}" is not declared by any plugin. Plugins must declare state keys in their "state" array.`);
|
|
2938
|
+
}
|
|
2939
|
+
__privateGet(this, _state).set(key, value);
|
|
2940
|
+
__privateGet(this, _scheduler).markState(key);
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* 读取 state(同步读取,始终返回最新值)。
|
|
2944
|
+
* @returns 对应 key 的当前值,不存在则返回 undefined
|
|
2945
|
+
*/
|
|
2946
|
+
getState(key) {
|
|
2947
|
+
return __privateGet(this, _state).get(key);
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* 导出完整 state 快照(调试/devtools 用)。
|
|
2951
|
+
* 返回每个 key 的当前值、所属插件和描述信息。
|
|
2952
|
+
*/
|
|
2953
|
+
dumpState() {
|
|
2954
|
+
const result = {};
|
|
2955
|
+
for (const [key, value] of __privateGet(this, _state)) {
|
|
2956
|
+
const meta = __privateGet(this, _stateMeta).get(key);
|
|
2957
|
+
result[key] = { value, owner: meta.owner, description: meta.description };
|
|
2958
|
+
}
|
|
2959
|
+
return result;
|
|
2960
|
+
}
|
|
2827
2961
|
/** 同步渲染一帧。适用于静态内容,仅重绘脏层 */
|
|
2828
2962
|
render() {
|
|
2829
2963
|
for (const layer of this.scene.layers) {
|
|
@@ -2832,10 +2966,12 @@ var _App = class _App {
|
|
|
2832
2966
|
}
|
|
2833
2967
|
}
|
|
2834
2968
|
}
|
|
2835
|
-
/**
|
|
2969
|
+
/** 请求异步渲染帧(幂等,一帧只执行一次)。无变化时自动停止 rAF 链 */
|
|
2836
2970
|
requestRender() {
|
|
2837
|
-
|
|
2838
|
-
|
|
2971
|
+
__privateSet(this, _renderDirty, true);
|
|
2972
|
+
if (__privateGet(this, _rafId) === null) {
|
|
2973
|
+
__privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
|
|
2974
|
+
}
|
|
2839
2975
|
}
|
|
2840
2976
|
/**
|
|
2841
2977
|
* 调整画布尺寸,同步更新所有层、容器和视口矩阵
|
|
@@ -2876,6 +3012,10 @@ var _App = class _App {
|
|
|
2876
3012
|
plugin.dispose?.();
|
|
2877
3013
|
}
|
|
2878
3014
|
__privateSet(this, _plugins, []);
|
|
3015
|
+
this.bus.removeAllListeners();
|
|
3016
|
+
__privateGet(this, _state).clear();
|
|
3017
|
+
__privateGet(this, _stateMeta).clear();
|
|
3018
|
+
__privateGet(this, _scheduler).clear();
|
|
2879
3019
|
if (__privateGet(this, _resizeObserver)) {
|
|
2880
3020
|
__privateGet(this, _resizeObserver).disconnect();
|
|
2881
3021
|
__privateSet(this, _resizeObserver, null);
|
|
@@ -2910,7 +3050,17 @@ var _App = class _App {
|
|
|
2910
3050
|
}
|
|
2911
3051
|
/** 序列化所有渲染层的场景图为 JSON,可用于保存/回放 */
|
|
2912
3052
|
toJSON() {
|
|
2913
|
-
|
|
3053
|
+
const json = serialize(this.scene.layers, this.cfg.width ?? 800, this.cfg.height ?? 600);
|
|
3054
|
+
const pluginsData = {};
|
|
3055
|
+
for (const plugin of __privateGet(this, _plugins)) {
|
|
3056
|
+
if (plugin.serialize) {
|
|
3057
|
+
pluginsData[plugin.name] = plugin.serialize();
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
if (Object.keys(pluginsData).length > 0) {
|
|
3061
|
+
json.plugins = pluginsData;
|
|
3062
|
+
}
|
|
3063
|
+
return json;
|
|
2914
3064
|
}
|
|
2915
3065
|
/**
|
|
2916
3066
|
* 从 JSON 快照创建新的 App 实例(静态工厂方法)
|
|
@@ -2949,17 +3099,49 @@ var _App = class _App {
|
|
|
2949
3099
|
__privateGet(this, _container).insertBefore(layer.renderer.el, __privateGet(this, _eventLayer).renderer.el);
|
|
2950
3100
|
}
|
|
2951
3101
|
}
|
|
3102
|
+
if (json.plugins) {
|
|
3103
|
+
for (const plugin of __privateGet(this, _plugins)) {
|
|
3104
|
+
const data = json.plugins[plugin.name];
|
|
3105
|
+
if (data && plugin.deserialize) {
|
|
3106
|
+
plugin.deserialize(data);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
2952
3110
|
}
|
|
2953
3111
|
};
|
|
2954
3112
|
_rafId = new WeakMap();
|
|
3113
|
+
_renderDirty = new WeakMap();
|
|
2955
3114
|
_mounted = new WeakMap();
|
|
2956
3115
|
_container = new WeakMap();
|
|
2957
3116
|
_eventLayer = new WeakMap();
|
|
2958
3117
|
_resizeObserver = new WeakMap();
|
|
2959
3118
|
_plugins = new WeakMap();
|
|
3119
|
+
_state = new WeakMap();
|
|
3120
|
+
_stateMeta = new WeakMap();
|
|
3121
|
+
_scheduler = new WeakMap();
|
|
2960
3122
|
_App_instances = new WeakSet();
|
|
2961
|
-
|
|
2962
|
-
|
|
3123
|
+
/**
|
|
3124
|
+
* 解析 zIndex:若已被占用则向上偏移到最近可用值。
|
|
3125
|
+
* 保证返回值不会与任何已有层的 layerIndex 冲突。
|
|
3126
|
+
*/
|
|
3127
|
+
resolveIndex_fn = function(desired) {
|
|
3128
|
+
const used = new Set(this.scene.layers.map((l) => l.layerIndex));
|
|
3129
|
+
while (used.has(desired)) desired++;
|
|
3130
|
+
return desired;
|
|
3131
|
+
};
|
|
3132
|
+
/**
|
|
3133
|
+
* 自动分配:当前最高非事件层 index + 1。
|
|
3134
|
+
* 保证返回值始终 >= 1(default 层固定为 0)。
|
|
3135
|
+
*/
|
|
3136
|
+
nextAvailableIndex_fn = function() {
|
|
3137
|
+
let max = 0;
|
|
3138
|
+
for (const l of this.scene.layers) {
|
|
3139
|
+
if (!l.isEventLayer && l.layerIndex > max) max = l.layerIndex;
|
|
3140
|
+
}
|
|
3141
|
+
return max + 1;
|
|
3142
|
+
};
|
|
3143
|
+
frame_fn = function(time) {
|
|
3144
|
+
__privateSet(this, _renderDirty, false);
|
|
2963
3145
|
this.scene.tick(time);
|
|
2964
3146
|
let anyDirty = false;
|
|
2965
3147
|
for (const layer of this.scene.layers) {
|
|
@@ -2968,8 +3150,10 @@ tick_fn = function(time) {
|
|
|
2968
3150
|
anyDirty = true;
|
|
2969
3151
|
}
|
|
2970
3152
|
}
|
|
2971
|
-
if (anyDirty) {
|
|
2972
|
-
this.
|
|
3153
|
+
if (anyDirty || __privateGet(this, _renderDirty)) {
|
|
3154
|
+
__privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
|
|
3155
|
+
} else {
|
|
3156
|
+
__privateSet(this, _rafId, null);
|
|
2973
3157
|
}
|
|
2974
3158
|
};
|
|
2975
3159
|
var App = _App;
|
|
@@ -3001,6 +3185,7 @@ var App = _App;
|
|
|
3001
3185
|
Renderer,
|
|
3002
3186
|
RoundShape,
|
|
3003
3187
|
Scene,
|
|
3188
|
+
Scheduler,
|
|
3004
3189
|
SectorShape,
|
|
3005
3190
|
SectorTransform,
|
|
3006
3191
|
Shape,
|
package/dist/main.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ import { vec2, mat2d } from 'gl-matrix';
|
|
|
2
2
|
import { AO, ClipPath, GF, Mat2d, Point, GradientOptions, IGraphicsRenderer, Size } from 'rendx-core';
|
|
3
3
|
import { BoundingBox } from 'rendx-bounding';
|
|
4
4
|
import { Path } from 'rendx-path';
|
|
5
|
+
import EventEmitter from 'eventemitter3';
|
|
5
6
|
|
|
6
7
|
type TransformStatus = 'start' | 'init' | 'waiting' | 'running' | 'last' | 'clear' | 'end';
|
|
7
8
|
/**
|
|
@@ -745,6 +746,8 @@ interface RendxJSON {
|
|
|
745
746
|
width: number;
|
|
746
747
|
height: number;
|
|
747
748
|
layers: LayerJSON[];
|
|
749
|
+
/** 插件序列化数据(key = plugin.name) */
|
|
750
|
+
plugins?: Record<string, Record<string, unknown>>;
|
|
748
751
|
}
|
|
749
752
|
interface LayerJSON {
|
|
750
753
|
name: string;
|
|
@@ -783,19 +786,60 @@ declare function serialize(layers: Layer[], width: number, height: number): Rend
|
|
|
783
786
|
/** 从 JSON 反序列化,创建层和节点(不包含 App 实例) */
|
|
784
787
|
declare function deserialize(json: RendxJSON, cfg: Partial<RendererConfig>): Layer[];
|
|
785
788
|
|
|
789
|
+
/**
|
|
790
|
+
* 插件 state 声明 — 每个插件声明自己管理的 state key、描述和初始值。
|
|
791
|
+
* App 在 use() 时校验 key 不冲突,并以初始值写入 state map。
|
|
792
|
+
*/
|
|
793
|
+
interface PluginStateDeclaration {
|
|
794
|
+
/** state key(推荐 `pluginName:field` 命名空间格式) */
|
|
795
|
+
key: string;
|
|
796
|
+
/** 描述(文档/调试用) */
|
|
797
|
+
description: string;
|
|
798
|
+
/** 初始值 */
|
|
799
|
+
initial: unknown;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* 插件图层声明 — 插件声明需要使用的渲染层。
|
|
803
|
+
* App 在 use() 时自动 acquireLayer(有则复用,无则创建)。
|
|
804
|
+
*/
|
|
805
|
+
interface PluginLayerDeclaration {
|
|
806
|
+
/** 层名称 */
|
|
807
|
+
name: string;
|
|
808
|
+
/**
|
|
809
|
+
* 全局层级意图(可选)。
|
|
810
|
+
*
|
|
811
|
+
* - 负数:在 default 层(index=0)之下(如 grid 背景层)
|
|
812
|
+
* - 正数:在 default 层之上(如节点层、选区层)
|
|
813
|
+
* - 同值冲突时 App 自动向上偏移,保证唯一
|
|
814
|
+
* - 省略时自动分配(当前最高层 + 1)
|
|
815
|
+
*/
|
|
816
|
+
zIndex?: number;
|
|
817
|
+
}
|
|
786
818
|
/**
|
|
787
819
|
* 插件接口
|
|
788
820
|
*
|
|
789
|
-
* 插件通过 `app.use(plugin)`
|
|
821
|
+
* 插件通过 `app.use(plugin)` 注册,框架会:
|
|
822
|
+
* 1. 校验并注册 state 声明
|
|
823
|
+
* 2. 自动创建/复用所需图层
|
|
824
|
+
* 3. 调用 `install(app)` 完成初始化
|
|
825
|
+
*
|
|
790
826
|
* 当 App dispose 时,若插件提供了 `dispose` 方法,则会自动调用。
|
|
791
827
|
*/
|
|
792
828
|
interface Plugin {
|
|
793
829
|
/** 插件名称(用于去重和调试) */
|
|
794
830
|
name: string;
|
|
831
|
+
/** 声明本插件管理的 state keys */
|
|
832
|
+
state?: PluginStateDeclaration[];
|
|
833
|
+
/** 声明需要使用的渲染层 */
|
|
834
|
+
layers?: PluginLayerDeclaration[];
|
|
795
835
|
/** 安装插件,在 `app.use()` 时调用 */
|
|
796
836
|
install(app: App): void;
|
|
797
837
|
/** 当 App resize 时调用 */
|
|
798
838
|
resize?(width: number, height: number): void;
|
|
839
|
+
/** 序列化插件自身状态(持久化) */
|
|
840
|
+
serialize?(): Record<string, unknown>;
|
|
841
|
+
/** 从持久化数据恢复插件状态 */
|
|
842
|
+
deserialize?(data: Record<string, unknown>): void;
|
|
799
843
|
/** 卸载插件,在 `app.dispose()` 时自动调用 */
|
|
800
844
|
dispose?(): void;
|
|
801
845
|
}
|
|
@@ -824,6 +868,12 @@ declare class App {
|
|
|
824
868
|
cfg: AppConfig;
|
|
825
869
|
scene: Scene;
|
|
826
870
|
observer: EventObserver;
|
|
871
|
+
/**
|
|
872
|
+
* 中心化事件总线 — 纯信号(不传数据)。
|
|
873
|
+
* 插件通过 `app.bus.emit('eventName')` 发布信号,
|
|
874
|
+
* 消费方通过 `app.bus.on('eventName', handler)` 订阅。
|
|
875
|
+
*/
|
|
876
|
+
bus: EventEmitter<string | symbol, any>;
|
|
827
877
|
/** 挂载容器(供插件访问) */
|
|
828
878
|
get container(): HTMLDivElement | null;
|
|
829
879
|
/** 是否已挂载 */
|
|
@@ -842,15 +892,53 @@ declare class App {
|
|
|
842
892
|
/** 获取层 */
|
|
843
893
|
getLayer(name: string): Layer | undefined;
|
|
844
894
|
/**
|
|
845
|
-
*
|
|
895
|
+
* 获取或创建图层(get-or-create 语义)。
|
|
896
|
+
* 多个插件声明同名图层时,只会创建一次。
|
|
897
|
+
*
|
|
898
|
+
* zIndex 语义:
|
|
899
|
+
* - 传入时代表全局层级意图(负数 = default 之下,正数 = default 之上)
|
|
900
|
+
* - 若与已有层冲突,自动向上偏移到最近可用值
|
|
901
|
+
* - 省略时自动分配(当前最高非事件层 + 1)
|
|
902
|
+
*
|
|
903
|
+
* @param name - 层名称
|
|
904
|
+
* @param zIndex - 全局层级意图(可选)
|
|
905
|
+
*/
|
|
906
|
+
acquireLayer(name: string, zIndex?: number): Layer;
|
|
907
|
+
/**
|
|
908
|
+
* 注册插件。流程:
|
|
909
|
+
* 1. 去重检查(同名跳过)
|
|
910
|
+
* 2. 注册 state 声明(key 冲突则抛错)
|
|
911
|
+
* 3. 自动 acquireLayer(声明的图层)
|
|
912
|
+
* 4. 调用 plugin.install(app)
|
|
913
|
+
*
|
|
846
914
|
* @param plugin - 实现 Plugin 接口的插件实例
|
|
847
915
|
*/
|
|
848
916
|
use(plugin: Plugin): this;
|
|
849
917
|
/** 获取已注册的插件 */
|
|
850
918
|
getPlugin(name: string): Plugin | undefined;
|
|
919
|
+
/**
|
|
920
|
+
* 写入 state(同步写入 + 异步批量通知)。
|
|
921
|
+
* key 必须先由某个插件在 `state[]` 中声明,否则抛错。
|
|
922
|
+
* 写入后立即可通过 `getState()` 读到最新值,通知在微任务中批量发出。
|
|
923
|
+
*/
|
|
924
|
+
setState(key: string, value: unknown): void;
|
|
925
|
+
/**
|
|
926
|
+
* 读取 state(同步读取,始终返回最新值)。
|
|
927
|
+
* @returns 对应 key 的当前值,不存在则返回 undefined
|
|
928
|
+
*/
|
|
929
|
+
getState<T>(key: string): T | undefined;
|
|
930
|
+
/**
|
|
931
|
+
* 导出完整 state 快照(调试/devtools 用)。
|
|
932
|
+
* 返回每个 key 的当前值、所属插件和描述信息。
|
|
933
|
+
*/
|
|
934
|
+
dumpState(): Record<string, {
|
|
935
|
+
value: unknown;
|
|
936
|
+
owner: string;
|
|
937
|
+
description: string;
|
|
938
|
+
}>;
|
|
851
939
|
/** 同步渲染一帧。适用于静态内容,仅重绘脏层 */
|
|
852
940
|
render(): void;
|
|
853
|
-
/**
|
|
941
|
+
/** 请求异步渲染帧(幂等,一帧只执行一次)。无变化时自动停止 rAF 链 */
|
|
854
942
|
requestRender(): void;
|
|
855
943
|
/**
|
|
856
944
|
* 调整画布尺寸,同步更新所有层、容器和视口矩阵
|
|
@@ -877,4 +965,24 @@ declare class App {
|
|
|
877
965
|
restoreFromJSON(json: RendxJSON): void;
|
|
878
966
|
}
|
|
879
967
|
|
|
880
|
-
|
|
968
|
+
/**
|
|
969
|
+
* 调度器 — 协调状态通知与渲染帧
|
|
970
|
+
*
|
|
971
|
+
* - state 变更通知:批量到微任务(同一事件循环内多次 setState 只通知一次)
|
|
972
|
+
* - 渲染帧:幂等 rAF,从不 cancel,通过 dirty 标记决定是否续帧
|
|
973
|
+
*/
|
|
974
|
+
declare class Scheduler {
|
|
975
|
+
#private;
|
|
976
|
+
constructor(onFlush: (keys: Set<string>) => void);
|
|
977
|
+
/**
|
|
978
|
+
* 标记 state key 变更,批量到微任务统一通知。
|
|
979
|
+
* 同一轮微任务内多次调用同一 key 只通知一次。
|
|
980
|
+
*/
|
|
981
|
+
markState(key: string): void;
|
|
982
|
+
/** 是否有待通知的 state 变更 */
|
|
983
|
+
get hasPending(): boolean;
|
|
984
|
+
/** 清空待通知队列(用于 dispose) */
|
|
985
|
+
clear(): void;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
export { App, type AppConfig, ArcShape, ArcTransform, AreaShape, AttributeTransform, Attributes, BaseTransform, type ChildJSON, CircleShape, ClipBoxTransform, CurveShape, EventDispatcher, type EventListener, type EventListenerOptions, EventObserver, EventTarget, Graphics, type GraphicsJSON, GraphicsTransform, Group, type GroupJSON, ImageShape, Layer, type LayerJSON, LineShape, Node, type NodeJSON, PathShape, type Plugin, type PluginLayerDeclaration, type PluginStateDeclaration, PolygonShape, RectShape, Renderer, type RendererConfig, type RendxJSON, RoundShape, Scene, Scheduler, SectorShape, SectorTransform, Shape, type ShapeCommand, SimulatedEvent, SymbolShape, type TextMeasureFn, TextShape, type TransformStatus, createShape, deserialize, imageLoader, isHit, serialize, serializeLayer, setCanvasRenderingContext2StrokeAttrs };
|
package/dist/main.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { vec2, mat2d } from 'gl-matrix';
|
|
|
2
2
|
import { AO, ClipPath, GF, Mat2d, Point, GradientOptions, IGraphicsRenderer, Size } from 'rendx-core';
|
|
3
3
|
import { BoundingBox } from 'rendx-bounding';
|
|
4
4
|
import { Path } from 'rendx-path';
|
|
5
|
+
import EventEmitter from 'eventemitter3';
|
|
5
6
|
|
|
6
7
|
type TransformStatus = 'start' | 'init' | 'waiting' | 'running' | 'last' | 'clear' | 'end';
|
|
7
8
|
/**
|
|
@@ -745,6 +746,8 @@ interface RendxJSON {
|
|
|
745
746
|
width: number;
|
|
746
747
|
height: number;
|
|
747
748
|
layers: LayerJSON[];
|
|
749
|
+
/** 插件序列化数据(key = plugin.name) */
|
|
750
|
+
plugins?: Record<string, Record<string, unknown>>;
|
|
748
751
|
}
|
|
749
752
|
interface LayerJSON {
|
|
750
753
|
name: string;
|
|
@@ -783,19 +786,60 @@ declare function serialize(layers: Layer[], width: number, height: number): Rend
|
|
|
783
786
|
/** 从 JSON 反序列化,创建层和节点(不包含 App 实例) */
|
|
784
787
|
declare function deserialize(json: RendxJSON, cfg: Partial<RendererConfig>): Layer[];
|
|
785
788
|
|
|
789
|
+
/**
|
|
790
|
+
* 插件 state 声明 — 每个插件声明自己管理的 state key、描述和初始值。
|
|
791
|
+
* App 在 use() 时校验 key 不冲突,并以初始值写入 state map。
|
|
792
|
+
*/
|
|
793
|
+
interface PluginStateDeclaration {
|
|
794
|
+
/** state key(推荐 `pluginName:field` 命名空间格式) */
|
|
795
|
+
key: string;
|
|
796
|
+
/** 描述(文档/调试用) */
|
|
797
|
+
description: string;
|
|
798
|
+
/** 初始值 */
|
|
799
|
+
initial: unknown;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* 插件图层声明 — 插件声明需要使用的渲染层。
|
|
803
|
+
* App 在 use() 时自动 acquireLayer(有则复用,无则创建)。
|
|
804
|
+
*/
|
|
805
|
+
interface PluginLayerDeclaration {
|
|
806
|
+
/** 层名称 */
|
|
807
|
+
name: string;
|
|
808
|
+
/**
|
|
809
|
+
* 全局层级意图(可选)。
|
|
810
|
+
*
|
|
811
|
+
* - 负数:在 default 层(index=0)之下(如 grid 背景层)
|
|
812
|
+
* - 正数:在 default 层之上(如节点层、选区层)
|
|
813
|
+
* - 同值冲突时 App 自动向上偏移,保证唯一
|
|
814
|
+
* - 省略时自动分配(当前最高层 + 1)
|
|
815
|
+
*/
|
|
816
|
+
zIndex?: number;
|
|
817
|
+
}
|
|
786
818
|
/**
|
|
787
819
|
* 插件接口
|
|
788
820
|
*
|
|
789
|
-
* 插件通过 `app.use(plugin)`
|
|
821
|
+
* 插件通过 `app.use(plugin)` 注册,框架会:
|
|
822
|
+
* 1. 校验并注册 state 声明
|
|
823
|
+
* 2. 自动创建/复用所需图层
|
|
824
|
+
* 3. 调用 `install(app)` 完成初始化
|
|
825
|
+
*
|
|
790
826
|
* 当 App dispose 时,若插件提供了 `dispose` 方法,则会自动调用。
|
|
791
827
|
*/
|
|
792
828
|
interface Plugin {
|
|
793
829
|
/** 插件名称(用于去重和调试) */
|
|
794
830
|
name: string;
|
|
831
|
+
/** 声明本插件管理的 state keys */
|
|
832
|
+
state?: PluginStateDeclaration[];
|
|
833
|
+
/** 声明需要使用的渲染层 */
|
|
834
|
+
layers?: PluginLayerDeclaration[];
|
|
795
835
|
/** 安装插件,在 `app.use()` 时调用 */
|
|
796
836
|
install(app: App): void;
|
|
797
837
|
/** 当 App resize 时调用 */
|
|
798
838
|
resize?(width: number, height: number): void;
|
|
839
|
+
/** 序列化插件自身状态(持久化) */
|
|
840
|
+
serialize?(): Record<string, unknown>;
|
|
841
|
+
/** 从持久化数据恢复插件状态 */
|
|
842
|
+
deserialize?(data: Record<string, unknown>): void;
|
|
799
843
|
/** 卸载插件,在 `app.dispose()` 时自动调用 */
|
|
800
844
|
dispose?(): void;
|
|
801
845
|
}
|
|
@@ -824,6 +868,12 @@ declare class App {
|
|
|
824
868
|
cfg: AppConfig;
|
|
825
869
|
scene: Scene;
|
|
826
870
|
observer: EventObserver;
|
|
871
|
+
/**
|
|
872
|
+
* 中心化事件总线 — 纯信号(不传数据)。
|
|
873
|
+
* 插件通过 `app.bus.emit('eventName')` 发布信号,
|
|
874
|
+
* 消费方通过 `app.bus.on('eventName', handler)` 订阅。
|
|
875
|
+
*/
|
|
876
|
+
bus: EventEmitter<string | symbol, any>;
|
|
827
877
|
/** 挂载容器(供插件访问) */
|
|
828
878
|
get container(): HTMLDivElement | null;
|
|
829
879
|
/** 是否已挂载 */
|
|
@@ -842,15 +892,53 @@ declare class App {
|
|
|
842
892
|
/** 获取层 */
|
|
843
893
|
getLayer(name: string): Layer | undefined;
|
|
844
894
|
/**
|
|
845
|
-
*
|
|
895
|
+
* 获取或创建图层(get-or-create 语义)。
|
|
896
|
+
* 多个插件声明同名图层时,只会创建一次。
|
|
897
|
+
*
|
|
898
|
+
* zIndex 语义:
|
|
899
|
+
* - 传入时代表全局层级意图(负数 = default 之下,正数 = default 之上)
|
|
900
|
+
* - 若与已有层冲突,自动向上偏移到最近可用值
|
|
901
|
+
* - 省略时自动分配(当前最高非事件层 + 1)
|
|
902
|
+
*
|
|
903
|
+
* @param name - 层名称
|
|
904
|
+
* @param zIndex - 全局层级意图(可选)
|
|
905
|
+
*/
|
|
906
|
+
acquireLayer(name: string, zIndex?: number): Layer;
|
|
907
|
+
/**
|
|
908
|
+
* 注册插件。流程:
|
|
909
|
+
* 1. 去重检查(同名跳过)
|
|
910
|
+
* 2. 注册 state 声明(key 冲突则抛错)
|
|
911
|
+
* 3. 自动 acquireLayer(声明的图层)
|
|
912
|
+
* 4. 调用 plugin.install(app)
|
|
913
|
+
*
|
|
846
914
|
* @param plugin - 实现 Plugin 接口的插件实例
|
|
847
915
|
*/
|
|
848
916
|
use(plugin: Plugin): this;
|
|
849
917
|
/** 获取已注册的插件 */
|
|
850
918
|
getPlugin(name: string): Plugin | undefined;
|
|
919
|
+
/**
|
|
920
|
+
* 写入 state(同步写入 + 异步批量通知)。
|
|
921
|
+
* key 必须先由某个插件在 `state[]` 中声明,否则抛错。
|
|
922
|
+
* 写入后立即可通过 `getState()` 读到最新值,通知在微任务中批量发出。
|
|
923
|
+
*/
|
|
924
|
+
setState(key: string, value: unknown): void;
|
|
925
|
+
/**
|
|
926
|
+
* 读取 state(同步读取,始终返回最新值)。
|
|
927
|
+
* @returns 对应 key 的当前值,不存在则返回 undefined
|
|
928
|
+
*/
|
|
929
|
+
getState<T>(key: string): T | undefined;
|
|
930
|
+
/**
|
|
931
|
+
* 导出完整 state 快照(调试/devtools 用)。
|
|
932
|
+
* 返回每个 key 的当前值、所属插件和描述信息。
|
|
933
|
+
*/
|
|
934
|
+
dumpState(): Record<string, {
|
|
935
|
+
value: unknown;
|
|
936
|
+
owner: string;
|
|
937
|
+
description: string;
|
|
938
|
+
}>;
|
|
851
939
|
/** 同步渲染一帧。适用于静态内容,仅重绘脏层 */
|
|
852
940
|
render(): void;
|
|
853
|
-
/**
|
|
941
|
+
/** 请求异步渲染帧(幂等,一帧只执行一次)。无变化时自动停止 rAF 链 */
|
|
854
942
|
requestRender(): void;
|
|
855
943
|
/**
|
|
856
944
|
* 调整画布尺寸,同步更新所有层、容器和视口矩阵
|
|
@@ -877,4 +965,24 @@ declare class App {
|
|
|
877
965
|
restoreFromJSON(json: RendxJSON): void;
|
|
878
966
|
}
|
|
879
967
|
|
|
880
|
-
|
|
968
|
+
/**
|
|
969
|
+
* 调度器 — 协调状态通知与渲染帧
|
|
970
|
+
*
|
|
971
|
+
* - state 变更通知:批量到微任务(同一事件循环内多次 setState 只通知一次)
|
|
972
|
+
* - 渲染帧:幂等 rAF,从不 cancel,通过 dirty 标记决定是否续帧
|
|
973
|
+
*/
|
|
974
|
+
declare class Scheduler {
|
|
975
|
+
#private;
|
|
976
|
+
constructor(onFlush: (keys: Set<string>) => void);
|
|
977
|
+
/**
|
|
978
|
+
* 标记 state key 变更,批量到微任务统一通知。
|
|
979
|
+
* 同一轮微任务内多次调用同一 key 只通知一次。
|
|
980
|
+
*/
|
|
981
|
+
markState(key: string): void;
|
|
982
|
+
/** 是否有待通知的 state 变更 */
|
|
983
|
+
get hasPending(): boolean;
|
|
984
|
+
/** 清空待通知队列(用于 dispose) */
|
|
985
|
+
clear(): void;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
export { App, type AppConfig, ArcShape, ArcTransform, AreaShape, AttributeTransform, Attributes, BaseTransform, type ChildJSON, CircleShape, ClipBoxTransform, CurveShape, EventDispatcher, type EventListener, type EventListenerOptions, EventObserver, EventTarget, Graphics, type GraphicsJSON, GraphicsTransform, Group, type GroupJSON, ImageShape, Layer, type LayerJSON, LineShape, Node, type NodeJSON, PathShape, type Plugin, type PluginLayerDeclaration, type PluginStateDeclaration, PolygonShape, RectShape, Renderer, type RendererConfig, type RendxJSON, RoundShape, Scene, Scheduler, SectorShape, SectorTransform, Shape, type ShapeCommand, SimulatedEvent, SymbolShape, type TextMeasureFn, TextShape, type TransformStatus, createShape, deserialize, imageLoader, isHit, serialize, serializeLayer, setCanvasRenderingContext2StrokeAttrs };
|
package/dist/main.js
CHANGED
|
@@ -2487,6 +2487,48 @@ createListener_fn = function(type) {
|
|
|
2487
2487
|
};
|
|
2488
2488
|
};
|
|
2489
2489
|
|
|
2490
|
+
// src/app.ts
|
|
2491
|
+
import EventEmitter2 from "eventemitter3";
|
|
2492
|
+
|
|
2493
|
+
// src/scheduler.ts
|
|
2494
|
+
var _pendingState, _microTaskQueued, _onFlush;
|
|
2495
|
+
var Scheduler = class {
|
|
2496
|
+
constructor(onFlush) {
|
|
2497
|
+
__privateAdd(this, _pendingState, /* @__PURE__ */ new Set());
|
|
2498
|
+
__privateAdd(this, _microTaskQueued, false);
|
|
2499
|
+
__privateAdd(this, _onFlush);
|
|
2500
|
+
__privateSet(this, _onFlush, onFlush);
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* 标记 state key 变更,批量到微任务统一通知。
|
|
2504
|
+
* 同一轮微任务内多次调用同一 key 只通知一次。
|
|
2505
|
+
*/
|
|
2506
|
+
markState(key) {
|
|
2507
|
+
__privateGet(this, _pendingState).add(key);
|
|
2508
|
+
if (!__privateGet(this, _microTaskQueued)) {
|
|
2509
|
+
__privateSet(this, _microTaskQueued, true);
|
|
2510
|
+
queueMicrotask(() => {
|
|
2511
|
+
__privateSet(this, _microTaskQueued, false);
|
|
2512
|
+
const keys = new Set(__privateGet(this, _pendingState));
|
|
2513
|
+
__privateGet(this, _pendingState).clear();
|
|
2514
|
+
__privateGet(this, _onFlush).call(this, keys);
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
/** 是否有待通知的 state 变更 */
|
|
2519
|
+
get hasPending() {
|
|
2520
|
+
return __privateGet(this, _pendingState).size > 0;
|
|
2521
|
+
}
|
|
2522
|
+
/** 清空待通知队列(用于 dispose) */
|
|
2523
|
+
clear() {
|
|
2524
|
+
__privateGet(this, _pendingState).clear();
|
|
2525
|
+
__privateSet(this, _microTaskQueued, false);
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
_pendingState = new WeakMap();
|
|
2529
|
+
_microTaskQueued = new WeakMap();
|
|
2530
|
+
_onFlush = new WeakMap();
|
|
2531
|
+
|
|
2490
2532
|
// src/serialization.ts
|
|
2491
2533
|
var SHAPE_FROM_KEYS = {
|
|
2492
2534
|
text: ["text", "x", "y"],
|
|
@@ -2648,21 +2690,39 @@ function deserialize(json, cfg) {
|
|
|
2648
2690
|
}
|
|
2649
2691
|
|
|
2650
2692
|
// src/app.ts
|
|
2651
|
-
var _rafId, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _App_instances,
|
|
2693
|
+
var _rafId, _renderDirty, _mounted, _container, _eventLayer, _resizeObserver, _plugins, _state, _stateMeta, _scheduler, _App_instances, resolveIndex_fn, nextAvailableIndex_fn, frame_fn;
|
|
2652
2694
|
var _App = class _App {
|
|
2653
2695
|
/**
|
|
2654
2696
|
* @param cfg - 引擎配置(width/height/layers/autoResize 等)
|
|
2655
2697
|
*/
|
|
2656
2698
|
constructor(cfg = {}) {
|
|
2657
2699
|
__privateAdd(this, _App_instances);
|
|
2700
|
+
/**
|
|
2701
|
+
* 中心化事件总线 — 纯信号(不传数据)。
|
|
2702
|
+
* 插件通过 `app.bus.emit('eventName')` 发布信号,
|
|
2703
|
+
* 消费方通过 `app.bus.on('eventName', handler)` 订阅。
|
|
2704
|
+
*/
|
|
2705
|
+
this.bus = new EventEmitter2();
|
|
2658
2706
|
__privateAdd(this, _rafId, null);
|
|
2707
|
+
__privateAdd(this, _renderDirty, false);
|
|
2659
2708
|
__privateAdd(this, _mounted, false);
|
|
2660
2709
|
__privateAdd(this, _container, null);
|
|
2661
2710
|
__privateAdd(this, _eventLayer);
|
|
2662
2711
|
__privateAdd(this, _resizeObserver, null);
|
|
2663
2712
|
__privateAdd(this, _plugins, []);
|
|
2713
|
+
// ========================
|
|
2714
|
+
// State Management
|
|
2715
|
+
// ========================
|
|
2716
|
+
__privateAdd(this, _state, /* @__PURE__ */ new Map());
|
|
2717
|
+
__privateAdd(this, _stateMeta, /* @__PURE__ */ new Map());
|
|
2718
|
+
__privateAdd(this, _scheduler);
|
|
2664
2719
|
this.cfg = cfg;
|
|
2665
2720
|
this.scene = new Scene();
|
|
2721
|
+
__privateSet(this, _scheduler, new Scheduler((keys) => {
|
|
2722
|
+
for (const key of keys) {
|
|
2723
|
+
this.bus.emit(`state:${key}`);
|
|
2724
|
+
}
|
|
2725
|
+
}));
|
|
2666
2726
|
__privateSet(this, _eventLayer, new Layer("__event__", 99999, this.cfg, true));
|
|
2667
2727
|
this.scene.registerLayer(__privateGet(this, _eventLayer));
|
|
2668
2728
|
this.scene.registerLayer(new Layer("default", 0, this.cfg));
|
|
@@ -2735,7 +2795,30 @@ var _App = class _App {
|
|
|
2735
2795
|
return this.scene.getLayer(name);
|
|
2736
2796
|
}
|
|
2737
2797
|
/**
|
|
2738
|
-
*
|
|
2798
|
+
* 获取或创建图层(get-or-create 语义)。
|
|
2799
|
+
* 多个插件声明同名图层时,只会创建一次。
|
|
2800
|
+
*
|
|
2801
|
+
* zIndex 语义:
|
|
2802
|
+
* - 传入时代表全局层级意图(负数 = default 之下,正数 = default 之上)
|
|
2803
|
+
* - 若与已有层冲突,自动向上偏移到最近可用值
|
|
2804
|
+
* - 省略时自动分配(当前最高非事件层 + 1)
|
|
2805
|
+
*
|
|
2806
|
+
* @param name - 层名称
|
|
2807
|
+
* @param zIndex - 全局层级意图(可选)
|
|
2808
|
+
*/
|
|
2809
|
+
acquireLayer(name, zIndex) {
|
|
2810
|
+
const existing = this.scene.getLayer(name);
|
|
2811
|
+
if (existing) return existing;
|
|
2812
|
+
const index = zIndex !== void 0 ? __privateMethod(this, _App_instances, resolveIndex_fn).call(this, zIndex) : __privateMethod(this, _App_instances, nextAvailableIndex_fn).call(this);
|
|
2813
|
+
return this.addLayer(name, index);
|
|
2814
|
+
}
|
|
2815
|
+
/**
|
|
2816
|
+
* 注册插件。流程:
|
|
2817
|
+
* 1. 去重检查(同名跳过)
|
|
2818
|
+
* 2. 注册 state 声明(key 冲突则抛错)
|
|
2819
|
+
* 3. 自动 acquireLayer(声明的图层)
|
|
2820
|
+
* 4. 调用 plugin.install(app)
|
|
2821
|
+
*
|
|
2739
2822
|
* @param plugin - 实现 Plugin 接口的插件实例
|
|
2740
2823
|
*/
|
|
2741
2824
|
use(plugin) {
|
|
@@ -2743,6 +2826,22 @@ var _App = class _App {
|
|
|
2743
2826
|
console.warn(`Plugin "${plugin.name}" is already registered.`);
|
|
2744
2827
|
return this;
|
|
2745
2828
|
}
|
|
2829
|
+
if (plugin.state) {
|
|
2830
|
+
for (const decl of plugin.state) {
|
|
2831
|
+
if (__privateGet(this, _stateMeta).has(decl.key)) {
|
|
2832
|
+
const owner = __privateGet(this, _stateMeta).get(decl.key).owner;
|
|
2833
|
+
throw new Error(`State key "${decl.key}" already declared by plugin "${owner}". Plugin "${plugin.name}" cannot redeclare it.`);
|
|
2834
|
+
}
|
|
2835
|
+
__privateGet(this, _stateMeta).set(decl.key, { owner: plugin.name, description: decl.description });
|
|
2836
|
+
__privateGet(this, _state).set(decl.key, decl.initial);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
if (plugin.layers) {
|
|
2840
|
+
const sorted = [...plugin.layers].sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0));
|
|
2841
|
+
for (const decl of sorted) {
|
|
2842
|
+
this.acquireLayer(decl.name, decl.zIndex);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2746
2845
|
__privateGet(this, _plugins).push(plugin);
|
|
2747
2846
|
plugin.install(this);
|
|
2748
2847
|
return this;
|
|
@@ -2751,6 +2850,40 @@ var _App = class _App {
|
|
|
2751
2850
|
getPlugin(name) {
|
|
2752
2851
|
return __privateGet(this, _plugins).find((p) => p.name === name);
|
|
2753
2852
|
}
|
|
2853
|
+
// ========================
|
|
2854
|
+
// Centralized State
|
|
2855
|
+
// ========================
|
|
2856
|
+
/**
|
|
2857
|
+
* 写入 state(同步写入 + 异步批量通知)。
|
|
2858
|
+
* key 必须先由某个插件在 `state[]` 中声明,否则抛错。
|
|
2859
|
+
* 写入后立即可通过 `getState()` 读到最新值,通知在微任务中批量发出。
|
|
2860
|
+
*/
|
|
2861
|
+
setState(key, value) {
|
|
2862
|
+
if (!__privateGet(this, _stateMeta).has(key)) {
|
|
2863
|
+
throw new Error(`State key "${key}" is not declared by any plugin. Plugins must declare state keys in their "state" array.`);
|
|
2864
|
+
}
|
|
2865
|
+
__privateGet(this, _state).set(key, value);
|
|
2866
|
+
__privateGet(this, _scheduler).markState(key);
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* 读取 state(同步读取,始终返回最新值)。
|
|
2870
|
+
* @returns 对应 key 的当前值,不存在则返回 undefined
|
|
2871
|
+
*/
|
|
2872
|
+
getState(key) {
|
|
2873
|
+
return __privateGet(this, _state).get(key);
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* 导出完整 state 快照(调试/devtools 用)。
|
|
2877
|
+
* 返回每个 key 的当前值、所属插件和描述信息。
|
|
2878
|
+
*/
|
|
2879
|
+
dumpState() {
|
|
2880
|
+
const result = {};
|
|
2881
|
+
for (const [key, value] of __privateGet(this, _state)) {
|
|
2882
|
+
const meta = __privateGet(this, _stateMeta).get(key);
|
|
2883
|
+
result[key] = { value, owner: meta.owner, description: meta.description };
|
|
2884
|
+
}
|
|
2885
|
+
return result;
|
|
2886
|
+
}
|
|
2754
2887
|
/** 同步渲染一帧。适用于静态内容,仅重绘脏层 */
|
|
2755
2888
|
render() {
|
|
2756
2889
|
for (const layer of this.scene.layers) {
|
|
@@ -2759,10 +2892,12 @@ var _App = class _App {
|
|
|
2759
2892
|
}
|
|
2760
2893
|
}
|
|
2761
2894
|
}
|
|
2762
|
-
/**
|
|
2895
|
+
/** 请求异步渲染帧(幂等,一帧只执行一次)。无变化时自动停止 rAF 链 */
|
|
2763
2896
|
requestRender() {
|
|
2764
|
-
|
|
2765
|
-
|
|
2897
|
+
__privateSet(this, _renderDirty, true);
|
|
2898
|
+
if (__privateGet(this, _rafId) === null) {
|
|
2899
|
+
__privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
|
|
2900
|
+
}
|
|
2766
2901
|
}
|
|
2767
2902
|
/**
|
|
2768
2903
|
* 调整画布尺寸,同步更新所有层、容器和视口矩阵
|
|
@@ -2803,6 +2938,10 @@ var _App = class _App {
|
|
|
2803
2938
|
plugin.dispose?.();
|
|
2804
2939
|
}
|
|
2805
2940
|
__privateSet(this, _plugins, []);
|
|
2941
|
+
this.bus.removeAllListeners();
|
|
2942
|
+
__privateGet(this, _state).clear();
|
|
2943
|
+
__privateGet(this, _stateMeta).clear();
|
|
2944
|
+
__privateGet(this, _scheduler).clear();
|
|
2806
2945
|
if (__privateGet(this, _resizeObserver)) {
|
|
2807
2946
|
__privateGet(this, _resizeObserver).disconnect();
|
|
2808
2947
|
__privateSet(this, _resizeObserver, null);
|
|
@@ -2837,7 +2976,17 @@ var _App = class _App {
|
|
|
2837
2976
|
}
|
|
2838
2977
|
/** 序列化所有渲染层的场景图为 JSON,可用于保存/回放 */
|
|
2839
2978
|
toJSON() {
|
|
2840
|
-
|
|
2979
|
+
const json = serialize(this.scene.layers, this.cfg.width ?? 800, this.cfg.height ?? 600);
|
|
2980
|
+
const pluginsData = {};
|
|
2981
|
+
for (const plugin of __privateGet(this, _plugins)) {
|
|
2982
|
+
if (plugin.serialize) {
|
|
2983
|
+
pluginsData[plugin.name] = plugin.serialize();
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
if (Object.keys(pluginsData).length > 0) {
|
|
2987
|
+
json.plugins = pluginsData;
|
|
2988
|
+
}
|
|
2989
|
+
return json;
|
|
2841
2990
|
}
|
|
2842
2991
|
/**
|
|
2843
2992
|
* 从 JSON 快照创建新的 App 实例(静态工厂方法)
|
|
@@ -2876,17 +3025,49 @@ var _App = class _App {
|
|
|
2876
3025
|
__privateGet(this, _container).insertBefore(layer.renderer.el, __privateGet(this, _eventLayer).renderer.el);
|
|
2877
3026
|
}
|
|
2878
3027
|
}
|
|
3028
|
+
if (json.plugins) {
|
|
3029
|
+
for (const plugin of __privateGet(this, _plugins)) {
|
|
3030
|
+
const data = json.plugins[plugin.name];
|
|
3031
|
+
if (data && plugin.deserialize) {
|
|
3032
|
+
plugin.deserialize(data);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
2879
3036
|
}
|
|
2880
3037
|
};
|
|
2881
3038
|
_rafId = new WeakMap();
|
|
3039
|
+
_renderDirty = new WeakMap();
|
|
2882
3040
|
_mounted = new WeakMap();
|
|
2883
3041
|
_container = new WeakMap();
|
|
2884
3042
|
_eventLayer = new WeakMap();
|
|
2885
3043
|
_resizeObserver = new WeakMap();
|
|
2886
3044
|
_plugins = new WeakMap();
|
|
3045
|
+
_state = new WeakMap();
|
|
3046
|
+
_stateMeta = new WeakMap();
|
|
3047
|
+
_scheduler = new WeakMap();
|
|
2887
3048
|
_App_instances = new WeakSet();
|
|
2888
|
-
|
|
2889
|
-
|
|
3049
|
+
/**
|
|
3050
|
+
* 解析 zIndex:若已被占用则向上偏移到最近可用值。
|
|
3051
|
+
* 保证返回值不会与任何已有层的 layerIndex 冲突。
|
|
3052
|
+
*/
|
|
3053
|
+
resolveIndex_fn = function(desired) {
|
|
3054
|
+
const used = new Set(this.scene.layers.map((l) => l.layerIndex));
|
|
3055
|
+
while (used.has(desired)) desired++;
|
|
3056
|
+
return desired;
|
|
3057
|
+
};
|
|
3058
|
+
/**
|
|
3059
|
+
* 自动分配:当前最高非事件层 index + 1。
|
|
3060
|
+
* 保证返回值始终 >= 1(default 层固定为 0)。
|
|
3061
|
+
*/
|
|
3062
|
+
nextAvailableIndex_fn = function() {
|
|
3063
|
+
let max = 0;
|
|
3064
|
+
for (const l of this.scene.layers) {
|
|
3065
|
+
if (!l.isEventLayer && l.layerIndex > max) max = l.layerIndex;
|
|
3066
|
+
}
|
|
3067
|
+
return max + 1;
|
|
3068
|
+
};
|
|
3069
|
+
frame_fn = function(time) {
|
|
3070
|
+
__privateSet(this, _renderDirty, false);
|
|
2890
3071
|
this.scene.tick(time);
|
|
2891
3072
|
let anyDirty = false;
|
|
2892
3073
|
for (const layer of this.scene.layers) {
|
|
@@ -2895,8 +3076,10 @@ tick_fn = function(time) {
|
|
|
2895
3076
|
anyDirty = true;
|
|
2896
3077
|
}
|
|
2897
3078
|
}
|
|
2898
|
-
if (anyDirty) {
|
|
2899
|
-
this.
|
|
3079
|
+
if (anyDirty || __privateGet(this, _renderDirty)) {
|
|
3080
|
+
__privateSet(this, _rafId, requestAnimationFrame((t) => __privateMethod(this, _App_instances, frame_fn).call(this, t)));
|
|
3081
|
+
} else {
|
|
3082
|
+
__privateSet(this, _rafId, null);
|
|
2900
3083
|
}
|
|
2901
3084
|
};
|
|
2902
3085
|
var App = _App;
|
|
@@ -2927,6 +3110,7 @@ export {
|
|
|
2927
3110
|
Renderer,
|
|
2928
3111
|
RoundShape,
|
|
2929
3112
|
Scene,
|
|
3113
|
+
Scheduler,
|
|
2930
3114
|
SectorShape,
|
|
2931
3115
|
SectorTransform,
|
|
2932
3116
|
Shape,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rendx-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.
|
|
36
|
-
"rendx-canvas": "^0.1.
|
|
37
|
-
"rendx-ease": "^0.1.
|
|
38
|
-
"rendx-interpolate": "^0.1.
|
|
39
|
-
"rendx-path": "^0.1.
|
|
40
|
-
"rendx-shape": "^0.1.
|
|
41
|
-
"rendx-svg": "^0.1.
|
|
42
|
-
"rendx-core": "^0.1.
|
|
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.1.1",
|
|
40
|
+
"rendx-shape": "^0.1.1",
|
|
41
|
+
"rendx-svg": "^0.1.1",
|
|
42
|
+
"rendx-core": "^0.1.1"
|
|
43
43
|
},
|
|
44
44
|
"sideEffects": false,
|
|
45
45
|
"files": [
|