@lightningtv/solid 3.0.0-2 → 3.0.0-21

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.
Files changed (206) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -0
  3. package/dist/src/activeElement.d.ts +1 -1
  4. package/dist/src/core/animation.d.ts +35 -0
  5. package/dist/src/core/animation.js +120 -0
  6. package/dist/src/core/animation.js.map +1 -0
  7. package/dist/src/core/config.d.ts +47 -0
  8. package/dist/src/core/config.js +23 -0
  9. package/dist/src/core/config.js.map +1 -0
  10. package/dist/src/core/domRenderer.d.ts +117 -0
  11. package/dist/src/core/domRenderer.js +1160 -0
  12. package/dist/src/core/domRenderer.js.map +1 -0
  13. package/dist/src/core/elementNode.d.ts +209 -0
  14. package/dist/src/core/elementNode.js +829 -0
  15. package/dist/src/core/elementNode.js.map +1 -0
  16. package/dist/src/core/flex.d.ts +2 -0
  17. package/dist/src/core/flex.js +243 -0
  18. package/dist/src/core/flex.js.map +1 -0
  19. package/dist/src/core/focusKeyTypes.d.ts +42 -0
  20. package/dist/src/core/focusKeyTypes.js +2 -0
  21. package/dist/src/core/focusKeyTypes.js.map +1 -0
  22. package/dist/src/core/focusManager.d.ts +13 -0
  23. package/dist/src/core/focusManager.js +269 -0
  24. package/dist/src/core/focusManager.js.map +1 -0
  25. package/dist/src/core/index.d.ts +12 -0
  26. package/dist/src/core/index.js +12 -0
  27. package/dist/src/core/index.js.map +1 -0
  28. package/dist/src/core/intrinsicTypes.d.ts +90 -0
  29. package/dist/src/core/intrinsicTypes.js +2 -0
  30. package/dist/src/core/intrinsicTypes.js.map +1 -0
  31. package/dist/src/core/lightningInit.d.ts +89 -0
  32. package/dist/src/core/lightningInit.js +26 -0
  33. package/dist/src/core/lightningInit.js.map +1 -0
  34. package/dist/src/core/nodeTypes.d.ts +6 -0
  35. package/dist/src/core/nodeTypes.js +6 -0
  36. package/dist/src/core/nodeTypes.js.map +1 -0
  37. package/dist/src/core/shaders.d.ts +51 -0
  38. package/dist/src/core/shaders.js +446 -0
  39. package/dist/src/core/shaders.js.map +1 -0
  40. package/dist/src/core/states.d.ts +12 -0
  41. package/dist/src/core/states.js +84 -0
  42. package/dist/src/core/states.js.map +1 -0
  43. package/dist/src/core/timings.d.ts +36 -0
  44. package/dist/src/core/timings.js +199 -0
  45. package/dist/src/core/timings.js.map +1 -0
  46. package/dist/src/core/utils.d.ts +39 -0
  47. package/dist/src/core/utils.js +164 -0
  48. package/dist/src/core/utils.js.map +1 -0
  49. package/dist/src/devtools/index.d.ts +1 -1
  50. package/dist/src/devtools/index.js +1 -1
  51. package/dist/src/devtools/index.js.map +1 -1
  52. package/dist/src/index.d.ts +3 -3
  53. package/dist/src/index.js +1 -1
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/jsx-runtime.d.ts +1 -3
  56. package/dist/src/primitives/Column.jsx +9 -10
  57. package/dist/src/primitives/Column.jsx.map +1 -1
  58. package/dist/src/primitives/FPSCounter.jsx +14 -1
  59. package/dist/src/primitives/FPSCounter.jsx.map +1 -1
  60. package/dist/src/primitives/Grid.d.ts +15 -6
  61. package/dist/src/primitives/Grid.jsx +35 -22
  62. package/dist/src/primitives/Grid.jsx.map +1 -1
  63. package/dist/src/primitives/Image.d.ts +8 -0
  64. package/dist/src/primitives/Image.jsx +24 -0
  65. package/dist/src/primitives/Image.jsx.map +1 -0
  66. package/dist/src/primitives/KeepAlive.d.ts +30 -0
  67. package/dist/src/primitives/KeepAlive.jsx +77 -0
  68. package/dist/src/primitives/KeepAlive.jsx.map +1 -0
  69. package/dist/src/primitives/Lazy.d.ts +8 -7
  70. package/dist/src/primitives/Lazy.jsx +52 -23
  71. package/dist/src/primitives/Lazy.jsx.map +1 -1
  72. package/dist/src/primitives/Marquee.d.ts +64 -0
  73. package/dist/src/primitives/Marquee.jsx +86 -0
  74. package/dist/src/primitives/Marquee.jsx.map +1 -0
  75. package/dist/src/primitives/Preserve.d.ts +4 -0
  76. package/dist/src/primitives/Preserve.jsx +11 -0
  77. package/dist/src/primitives/Preserve.jsx.map +1 -0
  78. package/dist/src/primitives/Row.jsx +9 -10
  79. package/dist/src/primitives/Row.jsx.map +1 -1
  80. package/dist/src/primitives/Suspense.d.ts +22 -0
  81. package/dist/src/primitives/Suspense.jsx +33 -0
  82. package/dist/src/primitives/Suspense.jsx.map +1 -0
  83. package/dist/src/primitives/Virtual.d.ts +18 -0
  84. package/dist/src/primitives/Virtual.jsx +434 -0
  85. package/dist/src/primitives/Virtual.jsx.map +1 -0
  86. package/dist/src/primitives/VirtualGrid.d.ts +13 -0
  87. package/dist/src/primitives/VirtualGrid.jsx +160 -0
  88. package/dist/src/primitives/VirtualGrid.jsx.map +1 -0
  89. package/dist/src/primitives/VirtualList.d.ts +11 -0
  90. package/dist/src/primitives/VirtualList.jsx +96 -0
  91. package/dist/src/primitives/VirtualList.jsx.map +1 -0
  92. package/dist/src/primitives/VirtualRow.d.ts +13 -0
  93. package/dist/src/primitives/VirtualRow.jsx +97 -0
  94. package/dist/src/primitives/VirtualRow.jsx.map +1 -0
  95. package/dist/src/primitives/Visible.d.ts +0 -1
  96. package/dist/src/primitives/Visible.jsx +1 -1
  97. package/dist/src/primitives/Visible.jsx.map +1 -1
  98. package/dist/src/primitives/announcer/announcer.d.ts +2 -0
  99. package/dist/src/primitives/announcer/announcer.js +7 -5
  100. package/dist/src/primitives/announcer/announcer.js.map +1 -1
  101. package/dist/src/primitives/announcer/index.d.ts +5 -1
  102. package/dist/src/primitives/announcer/index.js +8 -2
  103. package/dist/src/primitives/announcer/index.js.map +1 -1
  104. package/dist/src/primitives/announcer/speech.d.ts +2 -2
  105. package/dist/src/primitives/announcer/speech.js +157 -28
  106. package/dist/src/primitives/announcer/speech.js.map +1 -1
  107. package/dist/src/primitives/createFocusStack.d.ts +4 -4
  108. package/dist/src/primitives/createFocusStack.jsx +15 -6
  109. package/dist/src/primitives/createFocusStack.jsx.map +1 -1
  110. package/dist/src/primitives/createTag.d.ts +8 -0
  111. package/dist/src/primitives/createTag.jsx +20 -0
  112. package/dist/src/primitives/createTag.jsx.map +1 -0
  113. package/dist/src/primitives/index.d.ts +14 -4
  114. package/dist/src/primitives/index.js +13 -3
  115. package/dist/src/primitives/index.js.map +1 -1
  116. package/dist/src/primitives/types.d.ts +5 -2
  117. package/dist/src/primitives/useFocusManager.d.ts +2 -2
  118. package/dist/src/primitives/useFocusManager.js +2 -2
  119. package/dist/src/primitives/useFocusManager.js.map +1 -1
  120. package/dist/src/primitives/useHold.d.ts +27 -0
  121. package/dist/src/primitives/useHold.js +54 -0
  122. package/dist/src/primitives/useHold.js.map +1 -0
  123. package/dist/src/primitives/useMouse.d.ts +18 -2
  124. package/dist/src/primitives/useMouse.js +171 -47
  125. package/dist/src/primitives/useMouse.js.map +1 -1
  126. package/dist/src/primitives/utils/chainFunctions.d.ts +30 -4
  127. package/dist/src/primitives/utils/chainFunctions.js +14 -3
  128. package/dist/src/primitives/utils/chainFunctions.js.map +1 -1
  129. package/dist/src/primitives/utils/createBlurredImage.d.ts +56 -0
  130. package/dist/src/primitives/utils/createBlurredImage.js +223 -0
  131. package/dist/src/primitives/utils/createBlurredImage.js.map +1 -0
  132. package/dist/src/primitives/utils/createSpriteMap.d.ts +2 -2
  133. package/dist/src/primitives/utils/createSpriteMap.js +1 -1
  134. package/dist/src/primitives/utils/createSpriteMap.js.map +1 -1
  135. package/dist/src/primitives/utils/handleNavigation.d.ts +79 -5
  136. package/dist/src/primitives/utils/handleNavigation.js +242 -69
  137. package/dist/src/primitives/utils/handleNavigation.js.map +1 -1
  138. package/dist/src/primitives/utils/withScrolling.d.ts +14 -2
  139. package/dist/src/primitives/utils/withScrolling.js +66 -7
  140. package/dist/src/primitives/utils/withScrolling.js.map +1 -1
  141. package/dist/src/render.d.ts +8 -7
  142. package/dist/src/render.js +5 -1
  143. package/dist/src/render.js.map +1 -1
  144. package/dist/src/solidOpts.d.ts +1 -7
  145. package/dist/src/solidOpts.js +32 -16
  146. package/dist/src/solidOpts.js.map +1 -1
  147. package/dist/src/types.d.ts +1 -13
  148. package/dist/src/universal.d.ts +25 -0
  149. package/dist/src/universal.js +232 -0
  150. package/dist/src/universal.js.map +1 -0
  151. package/dist/src/utils.d.ts +3 -1
  152. package/dist/src/utils.js +9 -1
  153. package/dist/src/utils.js.map +1 -1
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/jsx-runtime.d.ts +2 -4
  156. package/package.json +17 -15
  157. package/src/activeElement.ts +1 -1
  158. package/src/core/animation.ts +183 -0
  159. package/src/core/config.ts +77 -0
  160. package/src/core/domRenderer.ts +1308 -0
  161. package/src/core/elementNode.ts +1198 -0
  162. package/src/core/flex.ts +284 -0
  163. package/src/core/focusKeyTypes.ts +87 -0
  164. package/src/core/focusManager.ts +359 -0
  165. package/src/core/index.ts +13 -0
  166. package/src/core/intrinsicTypes.ts +199 -0
  167. package/src/core/lightningInit.ts +147 -0
  168. package/src/core/nodeTypes.ts +6 -0
  169. package/src/core/shaders.ts +567 -0
  170. package/src/core/states.ts +91 -0
  171. package/src/core/timings.ts +261 -0
  172. package/src/core/utils.ts +222 -0
  173. package/src/devtools/index.ts +1 -1
  174. package/src/index.ts +3 -3
  175. package/src/primitives/Column.tsx +10 -12
  176. package/src/primitives/FPSCounter.tsx +15 -1
  177. package/src/primitives/Grid.tsx +57 -33
  178. package/src/primitives/Image.tsx +36 -0
  179. package/src/primitives/KeepAlive.tsx +124 -0
  180. package/src/primitives/Lazy.tsx +66 -37
  181. package/src/primitives/Marquee.tsx +149 -0
  182. package/src/primitives/Preserve.tsx +18 -0
  183. package/src/primitives/Row.tsx +13 -14
  184. package/src/primitives/Suspense.tsx +39 -0
  185. package/src/primitives/Virtual.tsx +478 -0
  186. package/src/primitives/VirtualGrid.tsx +220 -0
  187. package/src/primitives/Visible.tsx +1 -2
  188. package/src/primitives/announcer/announcer.ts +16 -10
  189. package/src/primitives/announcer/index.ts +12 -2
  190. package/src/primitives/announcer/speech.ts +188 -27
  191. package/src/primitives/createFocusStack.tsx +18 -7
  192. package/src/primitives/createTag.tsx +31 -0
  193. package/src/primitives/index.ts +18 -4
  194. package/src/primitives/types.ts +12 -2
  195. package/src/primitives/useFocusManager.ts +3 -3
  196. package/src/primitives/useHold.ts +69 -0
  197. package/src/primitives/useMouse.ts +306 -67
  198. package/src/primitives/utils/chainFunctions.ts +40 -9
  199. package/src/primitives/utils/createBlurredImage.ts +366 -0
  200. package/src/primitives/utils/createSpriteMap.ts +6 -4
  201. package/src/primitives/utils/handleNavigation.ts +300 -84
  202. package/src/primitives/utils/withScrolling.ts +91 -18
  203. package/src/render.ts +10 -8
  204. package/src/solidOpts.ts +31 -24
  205. package/src/types.ts +1 -15
  206. package/src/utils.ts +11 -1
@@ -0,0 +1,1308 @@
1
+ /*
2
+
3
+ Experimental DOM renderer
4
+
5
+ */
6
+
7
+ import * as lng from '@lightningjs/renderer';
8
+ import { EventEmitter } from '@lightningjs/renderer/utils';
9
+
10
+ import { Config } from './config.js';
11
+ import {
12
+ IRendererShader,
13
+ IRendererStage,
14
+ IRendererShaderProps,
15
+ IRendererTextureProps,
16
+ IRendererTexture,
17
+ IRendererMain,
18
+ IRendererNode,
19
+ IRendererNodeProps,
20
+ IRendererTextNode,
21
+ IRendererTextNodeProps,
22
+ } from './lightningInit.js';
23
+ import { isFunc } from './utils.js';
24
+
25
+ const colorToRgba = (c: number) =>
26
+ `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`;
27
+
28
+ function applyEasing(
29
+ easing: string | lng.TimingFunction,
30
+ progress: number,
31
+ ): number {
32
+ if (isFunc(easing)) {
33
+ return easing(progress);
34
+ }
35
+
36
+ switch (easing) {
37
+ case 'linear':
38
+ default:
39
+ return progress;
40
+ case 'ease-in':
41
+ return progress * progress;
42
+ case 'ease-out':
43
+ return progress * (2 - progress);
44
+ case 'ease-in-out':
45
+ return progress < 0.5
46
+ ? 2 * progress * progress
47
+ : -1 + (4 - 2 * progress) * progress;
48
+ }
49
+ }
50
+
51
+ function interpolate(start: number, end: number, t: number): number {
52
+ return start + (end - start) * t;
53
+ }
54
+
55
+ function interpolateColor(start: number, end: number, t: number): number {
56
+ return (
57
+ (interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) |
58
+ (interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) |
59
+ (interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) |
60
+ interpolate(start & 0xff, end & 0xff, t)
61
+ );
62
+ }
63
+
64
+ function interpolateProp(
65
+ name: string,
66
+ start: number,
67
+ end: number,
68
+ t: number,
69
+ ): number {
70
+ return name.startsWith('color')
71
+ ? interpolateColor(start, end, t)
72
+ : interpolate(start, end, t);
73
+ }
74
+
75
+ /*
76
+ Animations
77
+ */
78
+
79
+ let animationTasks: AnimationController[] = [];
80
+ let animationFrameRequested = false;
81
+
82
+ function requestAnimationUpdate() {
83
+ if (!animationFrameRequested && animationTasks.length > 0) {
84
+ animationFrameRequested = true;
85
+ requestAnimationFrame(updateAnimations);
86
+ }
87
+ }
88
+
89
+ function updateAnimations(time: number) {
90
+ animationFrameRequested = false;
91
+
92
+ /*
93
+ tasks are iterated in insertion order
94
+ so that the later task will override the earlier ones
95
+ */
96
+ for (let i = 0; i < animationTasks.length; i++) {
97
+ let task = animationTasks[i]!;
98
+ if (task.pausedTime != null) continue;
99
+
100
+ let elapsed = time - task.timeStart;
101
+
102
+ // Still in delay period
103
+ if (elapsed < task.settings.delay) {
104
+ requestAnimationUpdate();
105
+ continue;
106
+ }
107
+
108
+ let activeTime = elapsed - task.settings.delay;
109
+
110
+ if (activeTime >= task.settings.duration) {
111
+ // Start next iteration
112
+ if (task.settings.loop || task.iteration < task.settings.repeat - 1) {
113
+ task.iteration++;
114
+ task.timeStart = time - task.settings.delay;
115
+ requestAnimationUpdate();
116
+ }
117
+ // Animation complete
118
+ else {
119
+ Object.assign(task.node.props, task.propsEnd);
120
+ updateNodeStyles(task.node);
121
+
122
+ task.stop();
123
+ i--;
124
+ }
125
+ continue;
126
+ }
127
+
128
+ /*
129
+ Update props and styles
130
+ */
131
+ let t = activeTime / task.settings.duration;
132
+ t = applyEasing(task.settings.easing, t);
133
+
134
+ for (let prop in task.propsEnd) {
135
+ let start = task.propsStart[prop]!;
136
+ let end = task.propsEnd[prop]!;
137
+ (task.node.props as any)[prop] = interpolateProp(prop, start, end, t);
138
+ }
139
+
140
+ updateNodeStyles(task.node);
141
+ }
142
+
143
+ requestAnimationUpdate();
144
+ }
145
+
146
+ class AnimationController implements lng.IAnimationController {
147
+ state: lng.AnimationControllerState = 'paused';
148
+
149
+ stopPromise: Promise<void> | null = null;
150
+ stopResolve: (() => void) | null = null;
151
+
152
+ propsStart: Record<string, number> = {};
153
+ propsEnd: Record<string, number> = {};
154
+ timeStart: number = performance.now();
155
+ timeEnd: number;
156
+ settings: Required<lng.AnimationSettings>;
157
+ iteration: number = 0;
158
+ pausedTime: number | null = null;
159
+
160
+ constructor(
161
+ public node: DOMNode,
162
+ props: Partial<lng.INodeAnimateProps<any>>,
163
+ rawSettings: Partial<lng.AnimationSettings>,
164
+ ) {
165
+ this.settings = {
166
+ duration: rawSettings.duration ?? 300,
167
+ delay: rawSettings.delay ?? 0,
168
+ easing: rawSettings.easing ?? 'linear',
169
+ loop: rawSettings.loop ?? false,
170
+ repeat: rawSettings.repeat ?? 1,
171
+ stopMethod: false,
172
+ };
173
+
174
+ this.timeEnd =
175
+ this.timeStart + this.settings.delay + this.settings.duration;
176
+
177
+ for (let [prop, value] of Object.entries(props)) {
178
+ if (value != null && typeof value === 'number') {
179
+ this.propsStart[prop] = (node.props as any)[prop];
180
+ this.propsEnd[prop] = value;
181
+ }
182
+ }
183
+
184
+ animationTasks.push(this);
185
+ }
186
+
187
+ start() {
188
+ if (this.pausedTime != null) {
189
+ this.timeStart += performance.now() - this.pausedTime;
190
+ this.pausedTime = null;
191
+ } else {
192
+ this.timeStart = performance.now();
193
+ }
194
+ this.state = 'running';
195
+ requestAnimationUpdate();
196
+ return this;
197
+ }
198
+ pause() {
199
+ this.pausedTime = performance.now();
200
+ this.state = 'paused';
201
+ return this;
202
+ }
203
+ stop() {
204
+ let index = animationTasks.indexOf(this);
205
+ if (index !== -1) {
206
+ animationTasks.splice(index, 1);
207
+ }
208
+ this.state = 'stopped';
209
+ if (this.stopResolve) {
210
+ this.stopResolve();
211
+ this.stopResolve = null;
212
+ this.stopPromise = null;
213
+ }
214
+ return this;
215
+ }
216
+ restore() {
217
+ return this;
218
+ }
219
+ waitUntilStopped() {
220
+ this.stopPromise ??= new Promise((resolve) => {
221
+ this.stopResolve = resolve;
222
+ });
223
+ return this.stopPromise;
224
+ }
225
+ on() {
226
+ return this;
227
+ }
228
+ once() {
229
+ return this;
230
+ }
231
+ off() {
232
+ return this;
233
+ }
234
+ emit() {
235
+ return this;
236
+ }
237
+ }
238
+
239
+ function animate(
240
+ this: DOMNode,
241
+ props: Partial<lng.INodeAnimateProps<any>>,
242
+ settings: Partial<lng.AnimationSettings>,
243
+ ): lng.IAnimationController {
244
+ return new AnimationController(this, props, settings);
245
+ }
246
+
247
+ /*
248
+ Node Properties
249
+ */
250
+
251
+ let elMap = new WeakMap<DOMNode, HTMLElement>();
252
+
253
+ function updateNodeParent(node: DOMNode | DOMText) {
254
+ if (node.parent != null) {
255
+ elMap.get(node.parent as DOMNode)!.appendChild(node.div);
256
+ }
257
+ }
258
+
259
+ function getNodeLineHeight(props: IRendererTextNodeProps): number {
260
+ return (
261
+ props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize
262
+ );
263
+ }
264
+
265
+ function updateNodeStyles(node: DOMNode | DOMText) {
266
+ let { props } = node;
267
+
268
+ let style = `position: absolute; z-index: ${props.zIndex};`;
269
+
270
+ if (props.alpha !== 1) style += `opacity: ${props.alpha};`;
271
+
272
+ if (props.clipping) {
273
+ style += `overflow: hidden;`;
274
+ }
275
+
276
+ // Transform
277
+ {
278
+ let transform = '';
279
+
280
+ let { x, y } = props;
281
+
282
+ if (props.mountX != null) {
283
+ x -= (props.w ?? 0) * props.mountX;
284
+ }
285
+
286
+ if (props.mountY != null) {
287
+ y -= (props.h ?? 0) * props.mountY;
288
+ }
289
+
290
+ if (x !== 0) transform += `translateX(${x}px)`;
291
+
292
+ if (y !== 0) transform += `translateY(${y}px)`;
293
+
294
+ if (props.rotation !== 0) transform += `rotate(${props.rotation}rad)`;
295
+
296
+ if (props.scale !== 1 && props.scale != null) {
297
+ transform += `scale(${props.scale})`;
298
+ } else {
299
+ if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`;
300
+ if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`;
301
+ }
302
+
303
+ if (transform.length > 0) {
304
+ style += `transform: ${transform};`;
305
+ }
306
+ }
307
+
308
+ // <Text>
309
+ if (node instanceof DOMText) {
310
+ let textProps = node.props;
311
+
312
+ if (textProps.color != null && textProps.color !== 0) {
313
+ style += `color: ${colorToRgba(textProps.color)};`;
314
+ }
315
+ if (textProps.fontFamily) {
316
+ style += `font-family: ${textProps.fontFamily};`;
317
+ }
318
+ if (textProps.fontSize) {
319
+ style += `font-size: ${textProps.fontSize}px;`;
320
+ }
321
+ if (textProps.fontStyle !== 'normal') {
322
+ style += `font-style: ${textProps.fontStyle};`;
323
+ }
324
+ if (textProps.fontWeight !== 'normal') {
325
+ style += `font-weight: ${textProps.fontWeight};`;
326
+ }
327
+ if (textProps.lineHeight != null) {
328
+ style += `line-height: ${textProps.lineHeight}px;`;
329
+ }
330
+ if (textProps.letterSpacing) {
331
+ style += `letter-spacing: ${textProps.letterSpacing}px;`;
332
+ }
333
+ if (textProps.textAlign !== 'left') {
334
+ style += `text-align: ${textProps.textAlign};`;
335
+ }
336
+
337
+ let maxLines = textProps.maxLines || Infinity;
338
+ switch (textProps.contain) {
339
+ case 'width':
340
+ style += `width: ${props.w}px; overflow: hidden;`;
341
+ break;
342
+ case 'both': {
343
+ let lineHeight = getNodeLineHeight(textProps);
344
+ maxLines = Math.min(maxLines, Math.floor(props.h / lineHeight));
345
+ maxLines = Math.max(1, maxLines);
346
+ let height = maxLines * lineHeight;
347
+ style += `width: ${props.w}px; height: ${height}px; overflow: hidden;`;
348
+ break;
349
+ }
350
+ case 'none':
351
+ style += `width: max-content;`;
352
+ break;
353
+ }
354
+
355
+ if (maxLines !== Infinity) {
356
+ // https://stackoverflow.com/a/13924997
357
+ style += `display: -webkit-box;
358
+ overflow: hidden;
359
+ -webkit-line-clamp: ${maxLines};
360
+ line-clamp: ${maxLines};
361
+ -webkit-box-orient: vertical;`;
362
+ }
363
+
364
+ // if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};`
365
+ // if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};`
366
+
367
+ scheduleUpdateDOMTextMeasurement(node);
368
+ }
369
+ // <Node>
370
+ else {
371
+ if (props.w !== 0) style += `width: ${props.w}px;`;
372
+ if (props.h !== 0) style += `height: ${props.h}px;`;
373
+
374
+ let vGradient =
375
+ props.colorBottom !== props.colorTop
376
+ ? `linear-gradient(to bottom, ${colorToRgba(props.colorTop)}, ${colorToRgba(props.colorBottom)})`
377
+ : null;
378
+
379
+ let hGradient =
380
+ props.colorLeft !== props.colorRight
381
+ ? `linear-gradient(to right, ${colorToRgba(props.colorLeft)}, ${colorToRgba(props.colorRight)})`
382
+ : null;
383
+
384
+ let gradient =
385
+ vGradient && hGradient
386
+ ? `${vGradient}, ${hGradient}`
387
+ : vGradient || hGradient;
388
+
389
+ let srcImg: string | null = null;
390
+ let srcPos: null | { x: number; y: number } = null;
391
+
392
+ if (
393
+ props.texture != null &&
394
+ props.texture.type === lng.TextureType.subTexture
395
+ ) {
396
+ srcPos = (props.texture as any).props;
397
+ srcImg = `url(${(props.texture as any).props.texture.props.src})`;
398
+ } else if (props.src) {
399
+ srcImg = `url(${props.src})`;
400
+ }
401
+
402
+ let bgStyle = '';
403
+ let borderStyle = '';
404
+ let radiusStyle = '';
405
+ let maskStyle = '';
406
+
407
+ if (srcImg) {
408
+ if (props.color !== 0xffffffff && props.color !== 0x00000000) {
409
+ // use image as a mask
410
+ bgStyle += `background-color: ${colorToRgba(props.color)}; background-blend-mode: multiply;`;
411
+ maskStyle += `mask-image: ${srcImg};`;
412
+ if (srcPos !== null) {
413
+ maskStyle += `mask-position: -${srcPos.x}px -${srcPos.y}px;`;
414
+ } else {
415
+ maskStyle += `mask-size: 100% 100%;`;
416
+ }
417
+ } else if (gradient) {
418
+ // use gradient as a mask
419
+ maskStyle += `mask-image: ${gradient};`;
420
+ }
421
+
422
+ bgStyle += `background-image: ${srcImg};`;
423
+ bgStyle += `background-repeat: no-repeat;`;
424
+
425
+ if (props.textureOptions.resizeMode?.type) {
426
+ bgStyle += `background-size: ${props.textureOptions.resizeMode.type}; background-position: center;`;
427
+ } else if (srcPos !== null) {
428
+ bgStyle += `background-position: -${srcPos.x}px -${srcPos.y}px;`;
429
+ } else {
430
+ bgStyle += 'background-size: 100% 100%;';
431
+ }
432
+
433
+ if (maskStyle !== '') {
434
+ bgStyle += maskStyle;
435
+ }
436
+ // separate layers are needed for the mask
437
+ if (maskStyle !== '' && node.divBg == null) {
438
+ node.div.appendChild((node.divBg = document.createElement('div')));
439
+ node.div.appendChild((node.divBorder = document.createElement('div')));
440
+ }
441
+ } else if (gradient) {
442
+ bgStyle += `background-image: ${gradient};`;
443
+ bgStyle += `background-repeat: no-repeat;`;
444
+ bgStyle += `background-size: 100% 100%;`;
445
+ } else if (props.color !== 0) {
446
+ bgStyle += `background-color: ${colorToRgba(props.color)};`;
447
+ }
448
+
449
+ if (props.shader?.props != null) {
450
+ let shader = props.shader.props;
451
+
452
+ let borderWidth = shader['border-w'];
453
+ let borderColor = shader['border-color'];
454
+ let borderGap = shader['border-gap'] ?? 0;
455
+ let borderInset = shader['border-inset'] ?? true;
456
+ let radius = shader['radius'];
457
+
458
+ // Border
459
+ if (
460
+ typeof borderWidth === 'number' &&
461
+ borderWidth !== 0 &&
462
+ typeof borderColor === 'number' &&
463
+ borderColor !== 0
464
+ ) {
465
+ // Handle inset borders by making gap negative
466
+ let gap = borderInset ? -(borderWidth + borderGap) : borderGap;
467
+
468
+ borderStyle += `outline: ${borderWidth}px solid ${colorToRgba(borderColor)};`;
469
+ borderStyle += `outline-offset: ${gap}px;`;
470
+ }
471
+ // Rounded
472
+ if (typeof radius === 'number' && radius > 0) {
473
+ radiusStyle += `border-radius: ${radius}px;`;
474
+ } else if (Array.isArray(radius) && radius.length === 4) {
475
+ radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`;
476
+ }
477
+ }
478
+
479
+ style += radiusStyle;
480
+ bgStyle += radiusStyle;
481
+ borderStyle += radiusStyle;
482
+
483
+ if (node.divBg == null) {
484
+ style += bgStyle;
485
+ } else {
486
+ bgStyle += 'position: absolute; inset: 0; z-index: -1;';
487
+ node.divBg.setAttribute('style', bgStyle);
488
+ }
489
+ if (node.divBorder == null) {
490
+ style += borderStyle;
491
+ } else {
492
+ borderStyle += 'position: absolute; inset: 0; z-index: -1;';
493
+ node.divBorder.setAttribute('style', borderStyle);
494
+ }
495
+ }
496
+
497
+ node.div.setAttribute('style', style);
498
+ }
499
+
500
+ const fontFamiliesToLoad = new Set<string>();
501
+
502
+ const textNodesToMeasure = new Set<DOMText>();
503
+
504
+ type Size = { width: number; height: number };
505
+
506
+ function getElSize(node: DOMNode): Size {
507
+ let rect = node.div.getBoundingClientRect();
508
+
509
+ let dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1;
510
+ rect.height /= dpr;
511
+ rect.width /= dpr;
512
+
513
+ for (;;) {
514
+ if (node.props.scale != null && node.props.scale !== 1) {
515
+ rect.height /= node.props.scale;
516
+ rect.width /= node.props.scale;
517
+ } else {
518
+ rect.height /= node.props.scaleY;
519
+ rect.width /= node.props.scaleX;
520
+ }
521
+
522
+ if (node.parent instanceof DOMNode) {
523
+ node = node.parent;
524
+ } else {
525
+ break;
526
+ }
527
+ }
528
+
529
+ return rect;
530
+ }
531
+
532
+ /*
533
+ Text nodes with contain 'width' or 'none'
534
+ need to have their height or width calculated.
535
+ And then cause the flex layout to be recalculated.
536
+ */
537
+ function updateDOMTextSize(node: DOMText): void {
538
+ let size: Size;
539
+ switch (node.contain) {
540
+ case 'width':
541
+ size = getElSize(node);
542
+ if (node.props.h !== size.height) {
543
+ node.props.h = size.height;
544
+ updateNodeStyles(node);
545
+ node.emit('loaded');
546
+ }
547
+ break;
548
+ case 'none':
549
+ size = getElSize(node);
550
+ if (node.props.h !== size.height || node.props.w !== size.width) {
551
+ node.props.w = size.width;
552
+ node.props.h = size.height;
553
+ updateNodeStyles(node);
554
+ node.emit('loaded');
555
+ }
556
+ break;
557
+ }
558
+ }
559
+
560
+ function updateDOMTextMeasurements() {
561
+ textNodesToMeasure.forEach(updateDOMTextSize);
562
+ textNodesToMeasure.clear();
563
+ }
564
+
565
+ function scheduleUpdateDOMTextMeasurement(node: DOMText) {
566
+ /*
567
+ Make sure the font is loaded before measuring
568
+ */
569
+ if (node.fontFamily && !fontFamiliesToLoad.has(node.fontFamily)) {
570
+ fontFamiliesToLoad.add(node.fontFamily);
571
+ document.fonts.load(`16px ${node.fontFamily}`);
572
+ }
573
+
574
+ if (textNodesToMeasure.size === 0) {
575
+ if (document.fonts.status === 'loaded') {
576
+ setTimeout(updateDOMTextMeasurements);
577
+ } else {
578
+ document.fonts.ready.then(updateDOMTextMeasurements);
579
+ }
580
+ }
581
+
582
+ textNodesToMeasure.add(node);
583
+ }
584
+
585
+ function updateNodeData(node: DOMNode | DOMText) {
586
+ for (let key in node.data) {
587
+ let keyValue: unknown = node.data[key];
588
+ if (keyValue === undefined) {
589
+ node.div.removeAttribute('data-' + key);
590
+ } else {
591
+ node.div.setAttribute('data-' + key, String(keyValue));
592
+ }
593
+ }
594
+ }
595
+
596
+ function resolveNodeDefaults(
597
+ props: Partial<IRendererNodeProps>,
598
+ ): IRendererNodeProps {
599
+ const color = props.color ?? 0xffffffff;
600
+
601
+ return {
602
+ x: props.x ?? 0,
603
+ y: props.y ?? 0,
604
+ w: props.w ?? 0,
605
+ h: props.h ?? 0,
606
+ alpha: props.alpha ?? 1,
607
+ autosize: props.autosize ?? false,
608
+ boundsMargin: props.boundsMargin ?? null,
609
+ clipping: props.clipping ?? false,
610
+ color,
611
+ colorTop: props.colorTop ?? color,
612
+ colorBottom: props.colorBottom ?? color,
613
+ colorLeft: props.colorLeft ?? color,
614
+ colorRight: props.colorRight ?? color,
615
+ colorBl: props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color,
616
+ colorBr: props.colorBr ?? props.colorBottom ?? props.colorRight ?? color,
617
+ colorTl: props.colorTl ?? props.colorTop ?? props.colorLeft ?? color,
618
+ colorTr: props.colorTr ?? props.colorTop ?? props.colorRight ?? color,
619
+ zIndex: props.zIndex ?? 0,
620
+ zIndexLocked: props.zIndexLocked ?? 0,
621
+ parent: props.parent ?? null,
622
+ texture: props.texture ?? null,
623
+ textureOptions: props.textureOptions ?? {},
624
+ shader: props.shader ?? defaultShader,
625
+ // Since setting the `src` will trigger a texture load, we need to set it after
626
+ // we set the texture. Otherwise, problems happen.
627
+ src: props.src ?? null,
628
+ srcHeight: props.srcHeight,
629
+ srcWidth: props.srcWidth,
630
+ srcX: props.srcX,
631
+ srcY: props.srcY,
632
+ scale: props.scale ?? null,
633
+ scaleX: props.scaleX ?? props.scale ?? 1,
634
+ scaleY: props.scaleY ?? props.scale ?? 1,
635
+ mount: props.mount ?? 0,
636
+ mountX: props.mountX ?? props.mount ?? 0,
637
+ mountY: props.mountY ?? props.mount ?? 0,
638
+ pivot: props.pivot ?? 0.5,
639
+ pivotX: props.pivotX ?? props.pivot ?? 0.5,
640
+ pivotY: props.pivotY ?? props.pivot ?? 0.5,
641
+ rotation: props.rotation ?? 0,
642
+ rtt: props.rtt ?? false,
643
+ data: {},
644
+ imageType: props.imageType,
645
+ };
646
+ }
647
+
648
+ function resolveTextNodeDefaults(
649
+ props: Partial<IRendererTextNodeProps>,
650
+ ): IRendererTextNodeProps {
651
+ return {
652
+ ...resolveNodeDefaults(props),
653
+ text: props.text ?? '',
654
+ textRendererOverride: props.textRendererOverride ?? null,
655
+ fontSize: props.fontSize ?? 16,
656
+ fontFamily: props.fontFamily ?? 'sans-serif',
657
+ fontStyle: props.fontStyle ?? 'normal',
658
+ fontWeight: props.fontWeight ?? 'normal',
659
+ forceLoad: props.forceLoad ?? false,
660
+ textAlign: props.textAlign ?? 'left',
661
+ contain: props.contain ?? 'none',
662
+ offsetY: props.offsetY ?? 0,
663
+ letterSpacing: props.letterSpacing ?? 0,
664
+ lineHeight: props.lineHeight ?? 0,
665
+ maxLines: props.maxLines ?? 0,
666
+ maxWidth: props.maxWidth ?? 0,
667
+ maxHeight: props.maxHeight ?? 0,
668
+ verticalAlign: props.verticalAlign ?? 'middle',
669
+ overflowSuffix: props.overflowSuffix ?? '...',
670
+ wordBreak: props.wordBreak ?? 'overflow',
671
+ };
672
+ }
673
+
674
+ const defaultShader: IRendererShader = {
675
+ shaderType: '',
676
+ props: undefined,
677
+ };
678
+
679
+ let lastNodeId = 0;
680
+
681
+ class DOMNode extends EventEmitter implements IRendererNode {
682
+ div = document.createElement('div');
683
+ divBg: HTMLElement | undefined;
684
+ divBorder: HTMLElement | undefined;
685
+
686
+ id = ++lastNodeId;
687
+
688
+ renderState: lng.CoreNodeRenderState = 0 /* Init */;
689
+
690
+ constructor(
691
+ public stage: IRendererStage,
692
+ public props: IRendererNodeProps,
693
+ ) {
694
+ super();
695
+
696
+ // @ts-ignore
697
+ this.div._node = this;
698
+ this.div.setAttribute('data-id', String(this.id));
699
+ elMap.set(this, this.div);
700
+
701
+ updateNodeParent(this);
702
+ updateNodeStyles(this);
703
+ updateNodeData(this);
704
+ }
705
+
706
+ destroy(): void {
707
+ elMap.delete(this);
708
+ this.div.parentNode!.removeChild(this.div);
709
+ }
710
+
711
+ get parent() {
712
+ return this.props.parent;
713
+ }
714
+ set parent(value: IRendererNode | null) {
715
+ this.props.parent = value;
716
+ updateNodeParent(this);
717
+ }
718
+
719
+ animate = animate;
720
+
721
+ get x() {
722
+ return this.props.x;
723
+ }
724
+ set x(v) {
725
+ this.props.x = v;
726
+ updateNodeStyles(this);
727
+ }
728
+ get y() {
729
+ return this.props.y;
730
+ }
731
+ set y(v) {
732
+ this.props.y = v;
733
+ updateNodeStyles(this);
734
+ }
735
+ get w() {
736
+ return this.props.w;
737
+ }
738
+ set w(v) {
739
+ this.props.w = v;
740
+ updateNodeStyles(this);
741
+ }
742
+ get h() {
743
+ return this.props.h;
744
+ }
745
+ set h(v) {
746
+ this.props.h = v;
747
+ updateNodeStyles(this);
748
+ }
749
+ get width() {
750
+ return this.props.w;
751
+ }
752
+ set width(v) {
753
+ this.props.w = v;
754
+ updateNodeStyles(this);
755
+ }
756
+ get height() {
757
+ return this.props.h;
758
+ }
759
+ set height(v) {
760
+ this.props.h = v;
761
+ updateNodeStyles(this);
762
+ }
763
+ get alpha() {
764
+ return this.props.alpha;
765
+ }
766
+ set alpha(v) {
767
+ this.props.alpha = v;
768
+ updateNodeStyles(this);
769
+ }
770
+ get autosize() {
771
+ return this.props.autosize;
772
+ }
773
+ set autosize(v) {
774
+ this.props.autosize = v;
775
+ updateNodeStyles(this);
776
+ }
777
+ get clipping() {
778
+ return this.props.clipping;
779
+ }
780
+ set clipping(v) {
781
+ this.props.clipping = v;
782
+ updateNodeStyles(this);
783
+ }
784
+ get color() {
785
+ return this.props.color;
786
+ }
787
+ set color(v) {
788
+ this.props.color = v;
789
+ updateNodeStyles(this);
790
+ }
791
+ get colorTop() {
792
+ return this.props.colorTop;
793
+ }
794
+ set colorTop(v) {
795
+ this.props.colorTop = v;
796
+ updateNodeStyles(this);
797
+ }
798
+ get colorBottom() {
799
+ return this.props.colorBottom;
800
+ }
801
+ set colorBottom(v) {
802
+ this.props.colorBottom = v;
803
+ updateNodeStyles(this);
804
+ }
805
+ get colorLeft() {
806
+ return this.props.colorLeft;
807
+ }
808
+ set colorLeft(v) {
809
+ this.props.colorLeft = v;
810
+ updateNodeStyles(this);
811
+ }
812
+ get colorRight() {
813
+ return this.props.colorRight;
814
+ }
815
+ set colorRight(v) {
816
+ this.props.colorRight = v;
817
+ updateNodeStyles(this);
818
+ }
819
+ get colorTl() {
820
+ return this.props.colorTl;
821
+ }
822
+ set colorTl(v) {
823
+ this.props.colorTl = v;
824
+ updateNodeStyles(this);
825
+ }
826
+ get colorTr() {
827
+ return this.props.colorTr;
828
+ }
829
+ set colorTr(v) {
830
+ this.props.colorTr = v;
831
+ updateNodeStyles(this);
832
+ }
833
+ get colorBr() {
834
+ return this.props.colorBr;
835
+ }
836
+ set colorBr(v) {
837
+ this.props.colorBr = v;
838
+ updateNodeStyles(this);
839
+ }
840
+ get colorBl() {
841
+ return this.props.colorBl;
842
+ }
843
+ set colorBl(v) {
844
+ this.props.colorBl = v;
845
+ updateNodeStyles(this);
846
+ }
847
+ get zIndex() {
848
+ return this.props.zIndex;
849
+ }
850
+ set zIndex(v) {
851
+ this.props.zIndex = v;
852
+ updateNodeStyles(this);
853
+ }
854
+ get texture() {
855
+ return this.props.texture;
856
+ }
857
+ set texture(v) {
858
+ this.props.texture = v;
859
+ updateNodeStyles(this);
860
+ }
861
+ get textureOptions(): IRendererNode['textureOptions'] {
862
+ return this.props.textureOptions;
863
+ }
864
+ set textureOptions(v) {
865
+ this.props.textureOptions = v;
866
+ updateNodeStyles(this);
867
+ }
868
+ get src() {
869
+ return this.props.src;
870
+ }
871
+ set src(v) {
872
+ this.props.src = v;
873
+ updateNodeStyles(this);
874
+ }
875
+ get zIndexLocked() {
876
+ return this.props.zIndexLocked;
877
+ }
878
+ set zIndexLocked(v) {
879
+ this.props.zIndexLocked = v;
880
+ updateNodeStyles(this);
881
+ }
882
+ get scale() {
883
+ return this.props.scale ?? 1;
884
+ }
885
+ set scale(v) {
886
+ this.props.scale = v;
887
+ updateNodeStyles(this);
888
+ }
889
+ get scaleX() {
890
+ return this.props.scaleX;
891
+ }
892
+ set scaleX(v) {
893
+ this.props.scaleX = v;
894
+ updateNodeStyles(this);
895
+ }
896
+ get scaleY() {
897
+ return this.props.scaleY;
898
+ }
899
+ set scaleY(v) {
900
+ this.props.scaleY = v;
901
+ updateNodeStyles(this);
902
+ }
903
+ get mount() {
904
+ return this.props.mount;
905
+ }
906
+ set mount(v) {
907
+ this.props.mount = v;
908
+ updateNodeStyles(this);
909
+ }
910
+ get mountX() {
911
+ return this.props.mountX;
912
+ }
913
+ set mountX(v) {
914
+ this.props.mountX = v;
915
+ updateNodeStyles(this);
916
+ }
917
+ get mountY() {
918
+ return this.props.mountY;
919
+ }
920
+ set mountY(v) {
921
+ this.props.mountY = v;
922
+ updateNodeStyles(this);
923
+ }
924
+ get pivot() {
925
+ return this.props.pivot;
926
+ }
927
+ set pivot(v) {
928
+ this.props.pivot = v;
929
+ updateNodeStyles(this);
930
+ }
931
+ get pivotX() {
932
+ return this.props.pivotX;
933
+ }
934
+ set pivotX(v) {
935
+ this.props.pivotX = v;
936
+ updateNodeStyles(this);
937
+ }
938
+ get pivotY() {
939
+ return this.props.pivotY;
940
+ }
941
+ set pivotY(v) {
942
+ this.props.pivotY = v;
943
+ updateNodeStyles(this);
944
+ }
945
+ get rotation() {
946
+ return this.props.rotation;
947
+ }
948
+ set rotation(v) {
949
+ this.props.rotation = v;
950
+ updateNodeStyles(this);
951
+ }
952
+ get rtt() {
953
+ return this.props.rtt;
954
+ }
955
+ set rtt(v) {
956
+ this.props.rtt = v;
957
+ updateNodeStyles(this);
958
+ }
959
+ get shader() {
960
+ return this.props.shader;
961
+ }
962
+ set shader(v) {
963
+ this.props.shader = v;
964
+ updateNodeStyles(this);
965
+ }
966
+ get data(): IRendererNode['data'] {
967
+ return this.props.data;
968
+ }
969
+ set data(v) {
970
+ this.props.data = v;
971
+ updateNodeData(this);
972
+ }
973
+
974
+ get imageType() {
975
+ return this.props.imageType;
976
+ }
977
+ set imageType(v) {
978
+ this.props.imageType = v;
979
+ }
980
+ get srcWidth() {
981
+ return this.props.srcWidth;
982
+ }
983
+ set srcWidth(v) {
984
+ this.props.srcWidth = v;
985
+ }
986
+ get srcHeight() {
987
+ return this.props.srcHeight;
988
+ }
989
+ set srcHeight(v) {
990
+ this.props.srcHeight = v;
991
+ }
992
+ get srcX() {
993
+ return this.props.srcX;
994
+ }
995
+ set srcX(v) {
996
+ this.props.srcX = v;
997
+ }
998
+ get srcY() {
999
+ return this.props.srcY;
1000
+ }
1001
+ set srcY(v) {
1002
+ this.props.srcY = v;
1003
+ }
1004
+
1005
+ get boundsMargin(): number | [number, number, number, number] | null {
1006
+ return this.props.boundsMargin;
1007
+ }
1008
+ set boundsMargin(value: number | [number, number, number, number] | null) {
1009
+ this.props.boundsMargin = value;
1010
+ }
1011
+
1012
+ get absX(): number {
1013
+ return this.x + -this.width * this.mountX + (this.parent?.absX ?? 0);
1014
+ }
1015
+ get absY(): number {
1016
+ return this.y + -this.height * this.mountY + (this.parent?.absY ?? 0);
1017
+ }
1018
+ }
1019
+
1020
+ class DOMText extends DOMNode {
1021
+ constructor(
1022
+ stage: IRendererStage,
1023
+ public override props: IRendererTextNodeProps,
1024
+ ) {
1025
+ super(stage, props);
1026
+ this.div.innerText = props.text;
1027
+ }
1028
+
1029
+ get text() {
1030
+ return this.props.text;
1031
+ }
1032
+ set text(v) {
1033
+ this.props.text = v;
1034
+ this.div.innerText = v;
1035
+ scheduleUpdateDOMTextMeasurement(this);
1036
+ }
1037
+ get fontFamily() {
1038
+ return this.props.fontFamily;
1039
+ }
1040
+ set fontFamily(v) {
1041
+ this.props.fontFamily = v;
1042
+ updateNodeStyles(this);
1043
+ }
1044
+ get fontSize() {
1045
+ return this.props.fontSize;
1046
+ }
1047
+ set fontSize(v) {
1048
+ this.props.fontSize = v;
1049
+ updateNodeStyles(this);
1050
+ }
1051
+ get fontStyle() {
1052
+ return this.props.fontStyle;
1053
+ }
1054
+ set fontStyle(v) {
1055
+ this.props.fontStyle = v;
1056
+ updateNodeStyles(this);
1057
+ }
1058
+ get fontWeight() {
1059
+ return this.props.fontWeight;
1060
+ }
1061
+ set fontWeight(v) {
1062
+ this.props.fontWeight = v;
1063
+ updateNodeStyles(this);
1064
+ }
1065
+ get forceLoad() {
1066
+ return this.props.forceLoad;
1067
+ }
1068
+ set forceLoad(v) {
1069
+ this.props.forceLoad = v;
1070
+ }
1071
+ get lineHeight() {
1072
+ return this.props.lineHeight;
1073
+ }
1074
+ set lineHeight(v) {
1075
+ this.props.lineHeight = v;
1076
+ updateNodeStyles(this);
1077
+ }
1078
+ get maxWidth() {
1079
+ return this.props.maxWidth;
1080
+ }
1081
+ set maxWidth(v) {
1082
+ this.props.maxWidth = v;
1083
+ updateNodeStyles(this);
1084
+ }
1085
+ get maxHeight() {
1086
+ return this.props.maxHeight;
1087
+ }
1088
+ set maxHeight(v) {
1089
+ this.props.maxHeight = v;
1090
+ updateNodeStyles(this);
1091
+ }
1092
+ get letterSpacing() {
1093
+ return this.props.letterSpacing;
1094
+ }
1095
+ set letterSpacing(v) {
1096
+ this.props.letterSpacing = v;
1097
+ updateNodeStyles(this);
1098
+ }
1099
+ get textAlign() {
1100
+ return this.props.textAlign;
1101
+ }
1102
+ set textAlign(v) {
1103
+ this.props.textAlign = v;
1104
+ updateNodeStyles(this);
1105
+ }
1106
+ get overflowSuffix() {
1107
+ return this.props.overflowSuffix;
1108
+ }
1109
+ set overflowSuffix(v) {
1110
+ this.props.overflowSuffix = v;
1111
+ updateNodeStyles(this);
1112
+ }
1113
+ get maxLines() {
1114
+ return this.props.maxLines;
1115
+ }
1116
+ set maxLines(v) {
1117
+ this.props.maxLines = v;
1118
+ updateNodeStyles(this);
1119
+ }
1120
+ get contain() {
1121
+ return this.props.contain;
1122
+ }
1123
+ set contain(v) {
1124
+ this.props.contain = v;
1125
+ updateNodeStyles(this);
1126
+ }
1127
+ get verticalAlign() {
1128
+ return this.props.verticalAlign;
1129
+ }
1130
+ set verticalAlign(v) {
1131
+ this.props.verticalAlign = v;
1132
+ updateNodeStyles(this);
1133
+ }
1134
+ get textRendererOverride() {
1135
+ return this.props.textRendererOverride;
1136
+ }
1137
+ set textRendererOverride(v) {
1138
+ this.props.textRendererOverride = v;
1139
+ updateNodeStyles(this);
1140
+ }
1141
+ get offsetY() {
1142
+ return this.props.offsetY;
1143
+ }
1144
+ set offsetY(v) {
1145
+ this.props.offsetY = v;
1146
+ updateNodeStyles(this);
1147
+ }
1148
+ get wordBreak() {
1149
+ return this.props.wordBreak;
1150
+ }
1151
+ set wordBreak(v) {
1152
+ this.props.wordBreak = v;
1153
+ updateNodeStyles(this);
1154
+ }
1155
+ }
1156
+
1157
+ function updateRootPosition(this: DOMRendererMain) {
1158
+ let { canvas, settings } = this;
1159
+
1160
+ let rect = canvas.getBoundingClientRect();
1161
+ let top = document.documentElement.scrollTop + rect.top;
1162
+ let left = document.documentElement.scrollLeft + rect.left;
1163
+
1164
+ let dpr = settings.deviceLogicalPixelRatio ?? 1;
1165
+
1166
+ let height = Math.ceil(settings.appHeight ?? 1080 / dpr);
1167
+ let width = Math.ceil(settings.appWidth ?? 1920 / dpr);
1168
+
1169
+ this.root.div.style.left = `${left}px`;
1170
+ this.root.div.style.top = `${top}px`;
1171
+ this.root.div.style.width = `${width}px`;
1172
+ this.root.div.style.height = `${height}px`;
1173
+ this.root.div.style.position = 'absolute';
1174
+ this.root.div.style.transformOrigin = '0 0 0';
1175
+ this.root.div.style.transform = `scale(${dpr}, ${dpr})`;
1176
+ this.root.div.style.overflow = 'hidden';
1177
+ }
1178
+
1179
+ export class DOMRendererMain implements IRendererMain {
1180
+ root: DOMNode;
1181
+ canvas: HTMLCanvasElement;
1182
+
1183
+ stage: IRendererStage;
1184
+
1185
+ constructor(
1186
+ public settings: lng.RendererMainSettings,
1187
+ rawTarget: string | HTMLElement,
1188
+ ) {
1189
+ let target: HTMLElement;
1190
+ if (typeof rawTarget === 'string') {
1191
+ let result = document.getElementById(rawTarget);
1192
+ if (result instanceof HTMLElement) {
1193
+ target = result;
1194
+ } else {
1195
+ throw new Error(`Target #${rawTarget} not found`);
1196
+ }
1197
+ } else {
1198
+ target = rawTarget;
1199
+ }
1200
+
1201
+ let canvas = document.body.appendChild(document.createElement('canvas'));
1202
+ canvas.style.position = 'absolute';
1203
+ canvas.style.top = '0';
1204
+ canvas.style.left = '0';
1205
+ canvas.style.width = '100vw';
1206
+ canvas.style.height = '100vh';
1207
+
1208
+ this.canvas = canvas;
1209
+
1210
+ this.stage = {
1211
+ root: null!,
1212
+ renderer: {
1213
+ mode: 'canvas',
1214
+ },
1215
+ loadFont: async () => {},
1216
+ shManager: {
1217
+ registerShaderType() {},
1218
+ },
1219
+ animationManager: {
1220
+ registerAnimation() {},
1221
+ unregisterAnimation() {},
1222
+ },
1223
+ };
1224
+
1225
+ this.root = new DOMNode(
1226
+ this.stage,
1227
+ resolveNodeDefaults({
1228
+ w: settings.appWidth ?? 1920,
1229
+ h: settings.appHeight ?? 1080,
1230
+ shader: defaultShader,
1231
+ zIndex: 65534,
1232
+ }),
1233
+ );
1234
+ this.stage.root = this.root;
1235
+ target.appendChild(this.root.div);
1236
+
1237
+ if (Config.fontSettings.fontFamily) {
1238
+ this.root.div.style.fontFamily = Config.fontSettings.fontFamily;
1239
+ }
1240
+ if (Config.fontSettings.fontSize) {
1241
+ this.root.div.style.fontSize = Config.fontSettings.fontSize + 'px';
1242
+ }
1243
+ if (Config.fontSettings.lineHeight) {
1244
+ this.root.div.style.lineHeight = Config.fontSettings.lineHeight + 'px';
1245
+ } else {
1246
+ this.root.div.style.lineHeight = '1.2';
1247
+ }
1248
+ if (Config.fontSettings.fontWeight) {
1249
+ if (typeof Config.fontSettings.fontWeight === 'number') {
1250
+ this.root.div.style.fontWeight = Config.fontSettings.fontWeight + 'px';
1251
+ } else {
1252
+ this.root.div.style.fontWeight = Config.fontSettings.fontWeight;
1253
+ }
1254
+ }
1255
+
1256
+ updateRootPosition.call(this);
1257
+
1258
+ new MutationObserver(updateRootPosition.bind(this)).observe(this.canvas, {
1259
+ attributes: true,
1260
+ });
1261
+ new ResizeObserver(updateRootPosition.bind(this)).observe(this.canvas);
1262
+ window.addEventListener('resize', updateRootPosition.bind(this));
1263
+ }
1264
+
1265
+ createNode(props: Partial<IRendererNodeProps>): IRendererNode {
1266
+ return new DOMNode(this.stage, resolveNodeDefaults(props));
1267
+ }
1268
+
1269
+ createTextNode(props: Partial<IRendererTextNodeProps>): IRendererTextNode {
1270
+ return new DOMText(this.stage, resolveTextNodeDefaults(props));
1271
+ }
1272
+
1273
+ createShader(
1274
+ shaderType: string,
1275
+ props?: IRendererShaderProps,
1276
+ ): IRendererShader {
1277
+ return { shaderType, props, program: {} };
1278
+ }
1279
+
1280
+ createTexture(
1281
+ textureType: keyof lng.TextureMap,
1282
+ props: IRendererTextureProps,
1283
+ ): IRendererTexture {
1284
+ let type = lng.TextureType.generic;
1285
+ switch (textureType) {
1286
+ case 'SubTexture':
1287
+ type = lng.TextureType.subTexture;
1288
+ break;
1289
+ case 'ImageTexture':
1290
+ type = lng.TextureType.image;
1291
+ break;
1292
+ case 'ColorTexture':
1293
+ type = lng.TextureType.color;
1294
+ break;
1295
+ case 'NoiseTexture':
1296
+ type = lng.TextureType.noise;
1297
+ break;
1298
+ case 'RenderTexture':
1299
+ type = lng.TextureType.renderToTexture;
1300
+ break;
1301
+ }
1302
+ return { type, props };
1303
+ }
1304
+
1305
+ on(name: string, callback: (target: any, data: any) => void) {
1306
+ console.log('on', name, callback);
1307
+ }
1308
+ }