@@ -7,6 +7,8 @@ tags: ['RFC']
7
7
8
8
第一次提交版本,2024年1月20日。
9
9
10
+ 第二次提交版本,2024年1月24日,更新了有关切换类名的指令、模板数据结构和具体的 CSS 替换实现方案的内容。
11
+
10
12
## 总体方案
11
13
12
14
WebGAL 将使用模板思想进行 UI 自定义。在 WebGAL Terre 编辑器中,额外添加一个模板编辑器。在创建 WebGAL 游戏时和创建 WebGAL 游戏后,可以选择要使用的模板。
@@ -19,6 +21,7 @@ WebGAL 将使用模板思想进行 UI 自定义。在 WebGAL Terre 编辑器中
19
21
20
22
```
21
23
templateName/
24
+ ├── template.json
22
25
├── assets/
23
26
└── UI/
24
27
└── Title/
@@ -27,6 +30,16 @@ templateName/
27
30
28
31
其中,` assets ` 是资源目录。
29
32
33
+ 模板的数据结构是:
34
+
35
+ ``` json
36
+ {
37
+ "name" :" 模板名称" ,
38
+ "version" :" 4.4.10" ,
39
+ "其他字段" :" 待定......"
40
+ }
41
+ ```
42
+
30
43
### 模板配置文件定义
31
44
32
45
模板配置文件将覆盖 WebGAL 默认样式的一些类,通过动态创建 CSS 来实现。
@@ -65,6 +78,63 @@ useApplyStyle('UI/Title/title.css',{"Title_button":styles.Title_button});
65
78
66
79
这样就完成了对某个页面元素的 UI 自定义。
67
80
81
+ ### 切换类名的指令
82
+
83
+ 有时候,用户可能准备了多个类名以定义不同的样式,并希望使用脚本切换以达到某些表现效果。` applyStyle ` 指令可以切换类名。
84
+
85
+ ```
86
+ ; 将 Title_button 类切换到 Title_button_2 类
87
+ applyStyle:Title_button->Title_button_2;
88
+ ; 可以同时应用多个切换
89
+ applyStyle:Title_button->Title_button_2, Dialog->Dialog_1;
90
+ ```
91
+
92
+ ### 实现方法概览
93
+
94
+ 首先,在初始化模板时,WebGAL 维护一个从原始的模板中类名到 css module 生成的类名的映射:
95
+
96
+ ```
97
+ const styleMap = new Map<string,{targetClass:string, currentApplyClass:string}>();
98
+ ```
99
+
100
+ ` targetClass ` 代表要替换到的目标类名,比如 ` styles.Title_button ` (这是 css module 生成的,运行时会替换为一个随机字符串),` currentApplyClass ` 代表目前应用的类名,在初始化时与模板默认类名保持一致,但是可以被 ` applyStyle ` 指令切换。
101
+
102
+ 在注册时,就订阅类名切换的事件。如果某个插入的 css 段中的类名发生了“切换类名”,那么这个事件就会发出。指令会重新注册 ` currentApplyClass ` ,然后清除原有的 css 段,并重新字符串替换后插入新的 css 段。
103
+
104
+ 比如,原有的 ` styleMap ` 中有一个实体 ` "Title_button"->{targetClass:styles.Title_button, currentApplyClass:"Title_button"} `
105
+
106
+ 运行了指令` applyStyle:Title_button->Title_button_2; `
107
+
108
+ 此时更新 Map,注册为 ` Title_button"->{targetClass:styles.Title_button, currentApplyClass:"Title_button_2"} `
109
+
110
+ 这时候,要替换到 ` stytle.Title_button ` 的类名就变为 ` Title_button_2 ` ,原有的 ` Title_button ` 由于不被替换,所以无法生效。
111
+
112
+ 由此可见,切换类名的脚本要发出事件
113
+
114
+ ```
115
+ // ... 其他逻辑
116
+ eventBus.emit('classname-change',类名)
117
+ ```
118
+
119
+ ` useApplyStyle ` 要接受事件并判断是否要重新替换 CSS:
120
+
121
+ ``` ts
122
+ const useApplyStyle = (url : string ,classNameMap : Record <string ,string >) => {
123
+ useEffect (()=> {
124
+ const applyStyle = ()=> {
125
+ // ...... 其他代码
126
+ }
127
+ eventBus .on (' classname-change' ,className => {
128
+ const isHotReplace = Object .keys (classNameMap ).findIndex (e === className ) > - 1 ;
129
+ if (isHotReplace ) applyStyle ();
130
+ })
131
+ return ()=> {
132
+ eventBus .off (...... )
133
+ }
134
+ },[])
135
+ }
136
+ ```
137
+
68
138
### 引用资源
69
139
70
140
如果一个模板要引用资源,必须引用在模板资源目录下的资源。
@@ -145,10 +215,12 @@ registerStyleEditor('UI/Title/title.css', "Title_button", t("标题按钮")) //
145
215
146
216
``` ts
147
217
const useApplyStyle = (url : string ,classNameMap : Record <string ,string >) => {
148
- const applyStyle = ()=> {
218
+ useEffect (()=> {
219
+ const applyStyle = ()=> {
149
220
// ...... 其他代码
150
- }
151
- register (url , applyStyle );// 注册回调函数,当编辑器后端通知时,重新跑一遍 applyStyle
221
+ }
222
+ register (url , applyStyle );// 注册回调函数,当编辑器后端通知时,重新跑一遍 applyStyle
223
+ },[])
152
224
}
153
225
```
154
226
0 commit comments