@lightningtv/solid 3.1.7 → 3.1.8
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/src/core/animation.d.ts +1 -1
- package/dist/src/core/animation.js.map +1 -1
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/{domRenderer.d.ts → dom-renderer/domRenderer.d.ts} +30 -7
- package/dist/src/core/{domRenderer.js → dom-renderer/domRenderer.js} +633 -122
- package/dist/src/core/dom-renderer/domRenderer.js.map +1 -0
- package/dist/src/core/dom-renderer/domRendererTypes.d.ts +111 -0
- package/dist/src/core/dom-renderer/domRendererTypes.js +2 -0
- package/dist/src/core/dom-renderer/domRendererTypes.js.map +1 -0
- package/dist/src/core/dom-renderer/domRendererUtils.d.ts +23 -0
- package/dist/src/core/dom-renderer/domRendererUtils.js +231 -0
- package/dist/src/core/dom-renderer/domRendererUtils.js.map +1 -0
- package/dist/src/core/elementNode.d.ts +8 -8
- package/dist/src/core/elementNode.js +54 -15
- package/dist/src/core/elementNode.js.map +1 -1
- package/dist/src/core/index.d.ts +4 -2
- package/dist/src/core/index.js +1 -2
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/intrinsicTypes.d.ts +16 -6
- package/dist/src/core/lightningInit.d.ts +7 -89
- package/dist/src/core/lightningInit.js +13 -5
- package/dist/src/core/lightningInit.js.map +1 -1
- package/dist/src/core/shaders.d.ts +12 -11
- package/dist/src/core/shaders.js +0 -90
- package/dist/src/core/shaders.js.map +1 -1
- package/dist/src/primitives/Grid.jsx +2 -2
- package/dist/src/primitives/Grid.jsx.map +1 -1
- package/dist/src/primitives/Image.jsx +18 -0
- package/dist/src/primitives/Image.jsx.map +1 -1
- package/dist/src/render.d.ts +3 -3
- package/dist/src/render.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/core/animation.ts +6 -3
- package/src/core/config.ts +5 -2
- package/src/core/{domRenderer.ts → dom-renderer/domRenderer.ts} +738 -164
- package/src/core/dom-renderer/domRendererTypes.ts +150 -0
- package/src/core/dom-renderer/domRendererUtils.ts +291 -0
- package/src/core/elementNode.ts +98 -35
- package/src/core/index.ts +4 -2
- package/src/core/intrinsicTypes.ts +22 -6
- package/src/core/lightningInit.ts +20 -124
- package/src/core/shaders.ts +17 -110
- package/src/primitives/Grid.tsx +8 -7
- package/src/primitives/Image.tsx +20 -0
- package/src/render.ts +2 -1
- package/dist/src/core/domRenderer.js.map +0 -1
|
@@ -5,41 +5,16 @@ Experimental DOM renderer
|
|
|
5
5
|
*/
|
|
6
6
|
import * as lng from '@lightningjs/renderer';
|
|
7
7
|
import { EventEmitter } from '@lightningjs/renderer/utils';
|
|
8
|
-
import { Config } from '
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return progress;
|
|
19
|
-
case 'ease-in':
|
|
20
|
-
return progress * progress;
|
|
21
|
-
case 'ease-out':
|
|
22
|
-
return progress * (2 - progress);
|
|
23
|
-
case 'ease-in-out':
|
|
24
|
-
return progress < 0.5
|
|
25
|
-
? 2 * progress * progress
|
|
26
|
-
: -1 + (4 - 2 * progress) * progress;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function interpolate(start, end, t) {
|
|
30
|
-
return start + (end - start) * t;
|
|
31
|
-
}
|
|
32
|
-
function interpolateColor(start, end, t) {
|
|
33
|
-
return ((interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) |
|
|
34
|
-
(interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) |
|
|
35
|
-
(interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) |
|
|
36
|
-
interpolate(start & 0xff, end & 0xff, t));
|
|
37
|
-
}
|
|
38
|
-
function interpolateProp(name, start, end, t) {
|
|
39
|
-
return name.startsWith('color')
|
|
40
|
-
? interpolateColor(start, end, t)
|
|
41
|
-
: interpolate(start, end, t);
|
|
42
|
-
}
|
|
8
|
+
import { Config } from '../config.js';
|
|
9
|
+
import { colorToRgba, buildGradientStops, computeLegacyObjectFit, applySubTextureScaling, getNodeLineHeight, applyEasing, interpolateProp, isRenderStateInBounds, nodeHasTextureSource, computeRenderStateForNode, compactString, } from './domRendererUtils.js';
|
|
10
|
+
// Feature detection for legacy brousers
|
|
11
|
+
const _styleRef = typeof document !== 'undefined' ? document.documentElement?.style || {} : {};
|
|
12
|
+
const supportsObjectFit = 'objectFit' in _styleRef;
|
|
13
|
+
const supportsObjectPosition = 'objectPosition' in _styleRef;
|
|
14
|
+
const supportsMixBlendMode = 'mixBlendMode' in _styleRef;
|
|
15
|
+
const supportsStandardMask = 'maskImage' in _styleRef;
|
|
16
|
+
const supportsWebkitMask = 'webkitMaskImage' in _styleRef;
|
|
17
|
+
const supportsCssMask = supportsStandardMask || supportsWebkitMask;
|
|
43
18
|
/*
|
|
44
19
|
Animations
|
|
45
20
|
*/
|
|
@@ -78,6 +53,8 @@ function updateAnimations(time) {
|
|
|
78
53
|
// Animation complete
|
|
79
54
|
else {
|
|
80
55
|
Object.assign(task.node.props, task.propsEnd);
|
|
56
|
+
task.node.boundsDirty = true;
|
|
57
|
+
task.node.markChildrenBoundsDirty();
|
|
81
58
|
updateNodeStyles(task.node);
|
|
82
59
|
task.stop();
|
|
83
60
|
i--;
|
|
@@ -190,13 +167,11 @@ function animate(props, settings) {
|
|
|
190
167
|
*/
|
|
191
168
|
let elMap = new WeakMap();
|
|
192
169
|
function updateNodeParent(node) {
|
|
193
|
-
|
|
194
|
-
|
|
170
|
+
const parent = node.props.parent;
|
|
171
|
+
if (parent instanceof DOMNode) {
|
|
172
|
+
elMap.get(parent).appendChild(node.div);
|
|
195
173
|
}
|
|
196
174
|
}
|
|
197
|
-
function getNodeLineHeight(props) {
|
|
198
|
-
return (props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize);
|
|
199
|
-
}
|
|
200
175
|
function updateNodeStyles(node) {
|
|
201
176
|
let { props } = node;
|
|
202
177
|
let style = `position: absolute; z-index: ${props.zIndex};`;
|
|
@@ -252,6 +227,9 @@ function updateNodeStyles(node) {
|
|
|
252
227
|
if (textProps.fontWeight !== 'normal') {
|
|
253
228
|
style += `font-weight: ${textProps.fontWeight};`;
|
|
254
229
|
}
|
|
230
|
+
if (textProps.fontStretch && textProps.fontStretch !== 'normal') {
|
|
231
|
+
style += `font-stretch: ${textProps.fontStretch};`;
|
|
232
|
+
}
|
|
255
233
|
if (textProps.lineHeight != null) {
|
|
256
234
|
style += `line-height: ${textProps.lineHeight}px;`;
|
|
257
235
|
}
|
|
@@ -264,20 +242,41 @@ function updateNodeStyles(node) {
|
|
|
264
242
|
let maxLines = textProps.maxLines || Infinity;
|
|
265
243
|
switch (textProps.contain) {
|
|
266
244
|
case 'width':
|
|
267
|
-
|
|
245
|
+
if (textProps.maxWidth && textProps.maxWidth > 0) {
|
|
246
|
+
style += `width: ${textProps.maxWidth}px;`;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
style += `width: 100%;`;
|
|
250
|
+
}
|
|
251
|
+
style += `overflow: hidden;`;
|
|
268
252
|
break;
|
|
269
253
|
case 'both': {
|
|
270
254
|
let lineHeight = getNodeLineHeight(textProps);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
255
|
+
const widthConstraint = textProps.maxWidth && textProps.maxWidth > 0
|
|
256
|
+
? `${textProps.maxWidth}px`
|
|
257
|
+
: `100%`;
|
|
258
|
+
const heightConstraint = textProps.maxHeight && textProps.maxHeight > 0
|
|
259
|
+
? textProps.maxHeight
|
|
260
|
+
: props.h;
|
|
261
|
+
let height = heightConstraint || 0;
|
|
262
|
+
if (height > 0) {
|
|
263
|
+
const maxLinesByHeight = Math.max(1, Math.floor(height / lineHeight));
|
|
264
|
+
maxLines = Math.min(maxLines, maxLinesByHeight);
|
|
265
|
+
height = Math.max(lineHeight, maxLines * lineHeight);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
maxLines = Number.isFinite(maxLines) ? Math.max(1, maxLines) : 1;
|
|
269
|
+
height = maxLines * lineHeight;
|
|
270
|
+
}
|
|
271
|
+
style += `width: ${widthConstraint}; height: ${height}px; overflow: hidden;`;
|
|
275
272
|
break;
|
|
276
273
|
}
|
|
277
274
|
case 'none':
|
|
275
|
+
style += `width: -webkit-max-content;`;
|
|
278
276
|
style += `width: max-content;`;
|
|
279
277
|
break;
|
|
280
278
|
}
|
|
279
|
+
style += `white-space: pre-wrap;`;
|
|
281
280
|
if (maxLines !== Infinity) {
|
|
282
281
|
// https://stackoverflow.com/a/13924997
|
|
283
282
|
style += `display: -webkit-box;
|
|
@@ -288,12 +287,11 @@ function updateNodeStyles(node) {
|
|
|
288
287
|
}
|
|
289
288
|
// if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};`
|
|
290
289
|
// if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};`
|
|
291
|
-
scheduleUpdateDOMTextMeasurement(node);
|
|
292
290
|
}
|
|
293
291
|
// <Node>
|
|
294
292
|
else {
|
|
295
293
|
if (props.w !== 0)
|
|
296
|
-
style += `width: ${props.w}px;`;
|
|
294
|
+
style += `width: ${props.w < 0 ? 0 : props.w}px;`;
|
|
297
295
|
if (props.h !== 0)
|
|
298
296
|
style += `height: ${props.h}px;`;
|
|
299
297
|
let vGradient = props.colorBottom !== props.colorTop
|
|
@@ -307,53 +305,93 @@ function updateNodeStyles(node) {
|
|
|
307
305
|
: vGradient || hGradient;
|
|
308
306
|
let srcImg = null;
|
|
309
307
|
let srcPos = null;
|
|
308
|
+
let rawImgSrc = null;
|
|
310
309
|
if (props.texture != null &&
|
|
311
310
|
props.texture.type === lng.TextureType.subTexture) {
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
const texture = props.texture;
|
|
312
|
+
srcPos = texture.props;
|
|
313
|
+
rawImgSrc = texture.props.texture.props.src;
|
|
314
314
|
}
|
|
315
315
|
else if (props.src) {
|
|
316
|
-
|
|
316
|
+
rawImgSrc = props.src;
|
|
317
|
+
}
|
|
318
|
+
if (rawImgSrc) {
|
|
319
|
+
srcImg = `url(${rawImgSrc})`;
|
|
317
320
|
}
|
|
318
321
|
let bgStyle = '';
|
|
319
322
|
let borderStyle = '';
|
|
320
323
|
let radiusStyle = '';
|
|
321
324
|
let maskStyle = '';
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
maskStyle += `mask-
|
|
325
|
+
let needsBackgroundLayer = false;
|
|
326
|
+
let imgStyle = '';
|
|
327
|
+
let hasDivBgTint = false;
|
|
328
|
+
if (rawImgSrc) {
|
|
329
|
+
needsBackgroundLayer = true;
|
|
330
|
+
const hasTint = props.color !== 0xffffffff && props.color !== 0x00000000;
|
|
331
|
+
if (hasTint) {
|
|
332
|
+
bgStyle += `background-color: ${colorToRgba(props.color)};`;
|
|
333
|
+
if (srcImg) {
|
|
334
|
+
maskStyle += `mask-image: ${srcImg};`;
|
|
335
|
+
if (srcPos !== null) {
|
|
336
|
+
maskStyle += `mask-position: -${srcPos.x}px -${srcPos.y}px;`;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
maskStyle += `mask-size: 100% 100%;`;
|
|
340
|
+
}
|
|
341
|
+
hasDivBgTint = true;
|
|
332
342
|
}
|
|
333
343
|
}
|
|
334
344
|
else if (gradient) {
|
|
335
|
-
// use gradient as a mask
|
|
345
|
+
// use gradient as a mask when no tint is applied
|
|
336
346
|
maskStyle += `mask-image: ${gradient};`;
|
|
337
347
|
}
|
|
338
|
-
|
|
339
|
-
|
|
348
|
+
const imgStyleParts = [
|
|
349
|
+
'position: absolute',
|
|
350
|
+
'top: 0',
|
|
351
|
+
'left: 0',
|
|
352
|
+
'right: 0',
|
|
353
|
+
'bottom: 0',
|
|
354
|
+
'display: block',
|
|
355
|
+
'pointer-events: none',
|
|
356
|
+
];
|
|
340
357
|
if (props.textureOptions.resizeMode?.type) {
|
|
341
|
-
|
|
358
|
+
const resizeMode = props.textureOptions.resizeMode;
|
|
359
|
+
imgStyleParts.push('width: 100%');
|
|
360
|
+
imgStyleParts.push('height: 100%');
|
|
361
|
+
imgStyleParts.push(`object-fit: ${resizeMode.type}`);
|
|
362
|
+
// Handle clipX and clipY for object-position
|
|
363
|
+
const clipX = resizeMode.clipX ?? 0.5;
|
|
364
|
+
const clipY = resizeMode.clipY ?? 0.5;
|
|
365
|
+
imgStyleParts.push(`object-position: ${clipX * 100}% ${clipY * 100}%`);
|
|
342
366
|
}
|
|
343
367
|
else if (srcPos !== null) {
|
|
344
|
-
|
|
368
|
+
imgStyleParts.push('width: auto');
|
|
369
|
+
imgStyleParts.push('height: auto');
|
|
370
|
+
imgStyleParts.push('object-fit: none');
|
|
371
|
+
imgStyleParts.push(`object-position: -${srcPos.x}px -${srcPos.y}px`);
|
|
345
372
|
}
|
|
346
|
-
else {
|
|
347
|
-
|
|
373
|
+
else if (props.w && !props.h) {
|
|
374
|
+
imgStyleParts.push('width: 100%');
|
|
375
|
+
imgStyleParts.push('height: auto');
|
|
348
376
|
}
|
|
349
|
-
if (
|
|
350
|
-
|
|
377
|
+
else if (props.h && !props.w) {
|
|
378
|
+
imgStyleParts.push('width: auto');
|
|
379
|
+
imgStyleParts.push('height: 100%');
|
|
351
380
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
381
|
+
else {
|
|
382
|
+
imgStyleParts.push('width: 100%');
|
|
383
|
+
imgStyleParts.push('height: 100%');
|
|
384
|
+
imgStyleParts.push('object-fit: fill');
|
|
385
|
+
}
|
|
386
|
+
if (hasTint) {
|
|
387
|
+
if (supportsMixBlendMode) {
|
|
388
|
+
imgStyleParts.push('mix-blend-mode: multiply');
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
imgStyleParts.push('opacity: 0.9');
|
|
392
|
+
}
|
|
356
393
|
}
|
|
394
|
+
imgStyle = imgStyleParts.join('; ') + ';';
|
|
357
395
|
}
|
|
358
396
|
else if (gradient) {
|
|
359
397
|
bgStyle += `background-image: ${gradient};`;
|
|
@@ -364,20 +402,21 @@ function updateNodeStyles(node) {
|
|
|
364
402
|
bgStyle += `background-color: ${colorToRgba(props.color)};`;
|
|
365
403
|
}
|
|
366
404
|
if (props.shader?.props != null) {
|
|
367
|
-
let
|
|
368
|
-
let borderWidth =
|
|
369
|
-
let borderColor =
|
|
370
|
-
let borderGap =
|
|
371
|
-
let borderInset =
|
|
372
|
-
let radius =
|
|
405
|
+
let shaderProps = props.shader.props;
|
|
406
|
+
let borderWidth = shaderProps['border-w'];
|
|
407
|
+
let borderColor = shaderProps['border-color'];
|
|
408
|
+
let borderGap = shaderProps['border-gap'] ?? 0;
|
|
409
|
+
let borderInset = shaderProps['border-inset'] ?? true;
|
|
410
|
+
let radius = shaderProps['radius'];
|
|
373
411
|
// Border
|
|
374
412
|
if (typeof borderWidth === 'number' &&
|
|
375
413
|
borderWidth !== 0 &&
|
|
376
414
|
typeof borderColor === 'number' &&
|
|
377
415
|
borderColor !== 0) {
|
|
416
|
+
const rgbaColor = colorToRgba(borderColor);
|
|
378
417
|
// Handle inset borders by making gap negative
|
|
379
418
|
let gap = borderInset ? -(borderWidth + borderGap) : borderGap;
|
|
380
|
-
borderStyle += `outline: ${borderWidth}px solid ${
|
|
419
|
+
borderStyle += `outline: ${borderWidth}px solid ${rgbaColor};`;
|
|
381
420
|
borderStyle += `outline-offset: ${gap}px;`;
|
|
382
421
|
}
|
|
383
422
|
// Rounded
|
|
@@ -387,42 +426,254 @@ function updateNodeStyles(node) {
|
|
|
387
426
|
else if (Array.isArray(radius) && radius.length === 4) {
|
|
388
427
|
radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`;
|
|
389
428
|
}
|
|
429
|
+
if ('radial' in shaderProps) {
|
|
430
|
+
const rg = shaderProps.radial;
|
|
431
|
+
const colors = Array.isArray(rg?.colors) ? rg.colors : [];
|
|
432
|
+
const stops = Array.isArray(rg?.stops) ? rg.stops : undefined;
|
|
433
|
+
const pivot = Array.isArray(rg?.pivot) ? rg.pivot : [0.5, 0.5];
|
|
434
|
+
const width = typeof rg?.w === 'number' ? rg.w : props.w || 0;
|
|
435
|
+
const height = typeof rg?.h === 'number' ? rg.h : width;
|
|
436
|
+
if (colors.length > 0) {
|
|
437
|
+
const gradientStops = buildGradientStops(colors, stops);
|
|
438
|
+
if (gradientStops) {
|
|
439
|
+
if (colors.length === 1) {
|
|
440
|
+
// Single color -> solid fill
|
|
441
|
+
if (srcImg || gradient) {
|
|
442
|
+
maskStyle += `mask-image: linear-gradient(${gradientStops});`;
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
bgStyle += `background-color: ${colorToRgba(colors[0])};`;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
const isEllipse = width > 0 && height > 0 && width !== height;
|
|
450
|
+
const pivotX = (pivot[0] ?? 0.5) * 100;
|
|
451
|
+
const pivotY = (pivot[1] ?? 0.5) * 100;
|
|
452
|
+
let sizePart = '';
|
|
453
|
+
if (width > 0 && height > 0) {
|
|
454
|
+
if (!isEllipse && width === height) {
|
|
455
|
+
sizePart = `${Math.round(width)}px`;
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
sizePart = `${Math.round(width)}px ${Math.round(height)}px`;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
sizePart = 'closest-side';
|
|
463
|
+
}
|
|
464
|
+
const radialGradient = `radial-gradient(${isEllipse ? 'ellipse' : 'circle'} ${sizePart} at ${pivotX.toFixed(2)}% ${pivotY.toFixed(2)}%, ${gradientStops})`;
|
|
465
|
+
if (srcImg || gradient) {
|
|
466
|
+
maskStyle += `mask-image: ${radialGradient};`;
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
bgStyle += `background-image: ${radialGradient};`;
|
|
470
|
+
bgStyle += `background-repeat: no-repeat;`;
|
|
471
|
+
bgStyle += `background-size: 100% 100%;`;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if ('linear' in shaderProps) {
|
|
478
|
+
const lg = shaderProps.linear;
|
|
479
|
+
const colors = Array.isArray(lg?.colors) ? lg.colors : [];
|
|
480
|
+
const stops = Array.isArray(lg?.stops) ? lg.stops : undefined;
|
|
481
|
+
const angleRad = typeof lg?.angle === 'number' ? lg.angle : 0; // radians
|
|
482
|
+
if (colors.length > 0) {
|
|
483
|
+
const gradientStops = buildGradientStops(colors, stops);
|
|
484
|
+
if (gradientStops) {
|
|
485
|
+
if (colors.length === 1) {
|
|
486
|
+
if (srcImg || gradient) {
|
|
487
|
+
maskStyle += `mask-image: linear-gradient(${gradientStops});`;
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
bgStyle += `background-color: ${colorToRgba(colors[0])};`;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
const angleDeg = 180 * (angleRad / Math.PI - 1);
|
|
495
|
+
const linearGradient = `linear-gradient(${angleDeg.toFixed(2)}deg, ${gradientStops})`;
|
|
496
|
+
if (srcImg || gradient) {
|
|
497
|
+
maskStyle += `mask-image: ${linearGradient};`;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
bgStyle += `background-image: ${linearGradient};`;
|
|
501
|
+
bgStyle += `background-repeat: no-repeat;`;
|
|
502
|
+
bgStyle += `background-size: 100% 100%;`;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (maskStyle !== '') {
|
|
510
|
+
if (!supportsStandardMask && supportsWebkitMask) {
|
|
511
|
+
maskStyle = maskStyle.replace(/mask-/g, '-webkit-mask-');
|
|
512
|
+
}
|
|
513
|
+
else if (!supportsCssMask) {
|
|
514
|
+
maskStyle = '';
|
|
515
|
+
}
|
|
516
|
+
if (maskStyle !== '') {
|
|
517
|
+
needsBackgroundLayer = true;
|
|
518
|
+
}
|
|
390
519
|
}
|
|
391
520
|
style += radiusStyle;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
521
|
+
if (needsBackgroundLayer) {
|
|
522
|
+
if (node.divBg == null) {
|
|
523
|
+
node.divBg = document.createElement('div');
|
|
524
|
+
node.div.insertBefore(node.divBg, node.div.firstChild);
|
|
525
|
+
}
|
|
526
|
+
else if (node.divBg.parentElement !== node.div) {
|
|
527
|
+
node.div.insertBefore(node.divBg, node.div.firstChild);
|
|
528
|
+
}
|
|
529
|
+
let bgLayerStyle = 'position: absolute; top:0; left:0; right:0; bottom:0; z-index: -1; pointer-events: none; overflow: hidden;';
|
|
530
|
+
if (bgStyle) {
|
|
531
|
+
bgLayerStyle += bgStyle;
|
|
532
|
+
}
|
|
533
|
+
if (maskStyle) {
|
|
534
|
+
bgLayerStyle += maskStyle;
|
|
535
|
+
}
|
|
536
|
+
node.divBg.setAttribute('style', bgLayerStyle + radiusStyle);
|
|
537
|
+
if (rawImgSrc) {
|
|
538
|
+
if (!node.imgEl) {
|
|
539
|
+
node.imgEl = document.createElement('img');
|
|
540
|
+
node.imgEl.alt = '';
|
|
541
|
+
node.imgEl.crossOrigin = 'anonymous';
|
|
542
|
+
node.imgEl.setAttribute('aria-hidden', 'true');
|
|
543
|
+
node.imgEl.setAttribute('loading', 'lazy');
|
|
544
|
+
node.imgEl.removeAttribute('src');
|
|
545
|
+
node.imgEl.addEventListener('load', () => {
|
|
546
|
+
const payload = {
|
|
547
|
+
type: 'texture',
|
|
548
|
+
dimensions: {
|
|
549
|
+
w: node.imgEl.naturalWidth,
|
|
550
|
+
h: node.imgEl.naturalHeight,
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
node.imgEl.style.display = '';
|
|
554
|
+
applySubTextureScaling(node, node.imgEl, node.lazyImageSubTextureProps);
|
|
555
|
+
const resizeMode = node.props.textureOptions?.resizeMode;
|
|
556
|
+
const clipX = resizeMode?.clipX ?? 0.5;
|
|
557
|
+
const clipY = resizeMode?.clipY ?? 0.5;
|
|
558
|
+
computeLegacyObjectFit(node, node.imgEl, resizeMode, clipX, clipY, node.lazyImageSubTextureProps, supportsObjectFit, supportsObjectPosition);
|
|
559
|
+
node.emit('loaded', payload);
|
|
560
|
+
});
|
|
561
|
+
node.imgEl.addEventListener('error', () => {
|
|
562
|
+
if (node.imgEl) {
|
|
563
|
+
node.imgEl.removeAttribute('src');
|
|
564
|
+
node.imgEl.style.display = 'none';
|
|
565
|
+
node.imgEl.removeAttribute('data-rawSrc');
|
|
566
|
+
}
|
|
567
|
+
const failedSrc = node.imgEl?.dataset.pendingSrc || node.lazyImagePendingSrc || '';
|
|
568
|
+
const payload = {
|
|
569
|
+
type: 'texture',
|
|
570
|
+
error: new Error(`Failed to load image: ${failedSrc}`),
|
|
571
|
+
};
|
|
572
|
+
node.emit('failed', payload);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
node.lazyImagePendingSrc = rawImgSrc;
|
|
576
|
+
node.lazyImageSubTextureProps = srcPos;
|
|
577
|
+
node.imgEl.dataset.pendingSrc = rawImgSrc;
|
|
578
|
+
if (node.imgEl.parentElement !== node.divBg) {
|
|
579
|
+
node.divBg.appendChild(node.imgEl);
|
|
580
|
+
}
|
|
581
|
+
node.imgEl.setAttribute('style', imgStyle);
|
|
582
|
+
if (hasDivBgTint) {
|
|
583
|
+
node.imgEl.style.visibility = 'hidden';
|
|
584
|
+
}
|
|
585
|
+
if (isRenderStateInBounds(node.renderState)) {
|
|
586
|
+
node.applyPendingImageSrc();
|
|
587
|
+
}
|
|
588
|
+
else if (!node.imgEl.dataset.rawSrc) {
|
|
589
|
+
node.imgEl.removeAttribute('src');
|
|
590
|
+
}
|
|
591
|
+
if (srcPos &&
|
|
592
|
+
node.imgEl.complete &&
|
|
593
|
+
node.imgEl.dataset.rawSrc === rawImgSrc) {
|
|
594
|
+
applySubTextureScaling(node, node.imgEl, srcPos);
|
|
595
|
+
}
|
|
596
|
+
if (!srcPos &&
|
|
597
|
+
node.imgEl.complete &&
|
|
598
|
+
(!supportsObjectFit || !supportsObjectPosition) &&
|
|
599
|
+
node.imgEl.dataset.rawSrc === rawImgSrc) {
|
|
600
|
+
const resizeMode = node.props.textureOptions?.resizeMode;
|
|
601
|
+
const clipX = resizeMode?.clipX ?? 0.5;
|
|
602
|
+
const clipY = resizeMode?.clipY ?? 0.5;
|
|
603
|
+
computeLegacyObjectFit(node, node.imgEl, resizeMode, clipX, clipY, srcPos, supportsObjectFit, supportsObjectPosition);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
node.lazyImagePendingSrc = null;
|
|
608
|
+
node.lazyImageSubTextureProps = null;
|
|
609
|
+
if (node.imgEl) {
|
|
610
|
+
node.imgEl.remove();
|
|
611
|
+
node.imgEl = undefined;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
396
614
|
}
|
|
397
615
|
else {
|
|
398
|
-
|
|
399
|
-
node.
|
|
616
|
+
node.lazyImagePendingSrc = null;
|
|
617
|
+
node.lazyImageSubTextureProps = null;
|
|
618
|
+
if (node.imgEl) {
|
|
619
|
+
node.imgEl.remove();
|
|
620
|
+
node.imgEl = undefined;
|
|
621
|
+
}
|
|
622
|
+
if (node.divBg) {
|
|
623
|
+
node.divBg.remove();
|
|
624
|
+
node.divBg = undefined;
|
|
625
|
+
}
|
|
626
|
+
style += bgStyle;
|
|
627
|
+
}
|
|
628
|
+
const needsSeparateBorderLayer = needsBackgroundLayer && maskStyle !== '';
|
|
629
|
+
if (needsSeparateBorderLayer) {
|
|
630
|
+
if (node.divBorder == null) {
|
|
631
|
+
node.divBorder = document.createElement('div');
|
|
632
|
+
node.div.appendChild(node.divBorder);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else if (node.divBorder) {
|
|
636
|
+
node.divBorder.remove();
|
|
637
|
+
node.divBorder = undefined;
|
|
400
638
|
}
|
|
401
639
|
if (node.divBorder == null) {
|
|
402
640
|
style += borderStyle;
|
|
403
641
|
}
|
|
404
642
|
else {
|
|
405
|
-
|
|
406
|
-
|
|
643
|
+
let borderLayerStyle = 'position: absolute; top:0; left:0; right:0; bottom:0; z-index: -1; pointer-events: none;';
|
|
644
|
+
borderLayerStyle += borderStyle;
|
|
645
|
+
node.divBorder.setAttribute('style', borderLayerStyle + radiusStyle);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
node.div.setAttribute('style', compactString(style));
|
|
649
|
+
if (node instanceof DOMNode && node !== node.stage.root) {
|
|
650
|
+
const hasTextureSrc = nodeHasTextureSource(node);
|
|
651
|
+
if (hasTextureSrc && node.boundsDirty) {
|
|
652
|
+
const next = computeRenderStateForNode(node);
|
|
653
|
+
if (next != null) {
|
|
654
|
+
node.updateRenderState(next);
|
|
655
|
+
}
|
|
656
|
+
node.boundsDirty = false;
|
|
657
|
+
}
|
|
658
|
+
else if (!hasTextureSrc) {
|
|
659
|
+
node.boundsDirty = false;
|
|
407
660
|
}
|
|
408
661
|
}
|
|
409
|
-
node.div.setAttribute('style', style);
|
|
410
662
|
}
|
|
411
|
-
const fontFamiliesToLoad = new Set();
|
|
412
663
|
const textNodesToMeasure = new Set();
|
|
413
664
|
function getElSize(node) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
665
|
+
const rawRect = node.div.getBoundingClientRect();
|
|
666
|
+
const dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1;
|
|
667
|
+
let width = rawRect.width / dpr;
|
|
668
|
+
let height = rawRect.height / dpr;
|
|
418
669
|
for (;;) {
|
|
419
670
|
if (node.props.scale != null && node.props.scale !== 1) {
|
|
420
|
-
|
|
421
|
-
|
|
671
|
+
width /= node.props.scale;
|
|
672
|
+
height /= node.props.scale;
|
|
422
673
|
}
|
|
423
674
|
else {
|
|
424
|
-
|
|
425
|
-
|
|
675
|
+
width /= node.props.scaleX;
|
|
676
|
+
height /= node.props.scaleY;
|
|
426
677
|
}
|
|
427
678
|
if (node.parent instanceof DOMNode) {
|
|
428
679
|
node = node.parent;
|
|
@@ -431,7 +682,7 @@ function getElSize(node) {
|
|
|
431
682
|
break;
|
|
432
683
|
}
|
|
433
684
|
}
|
|
434
|
-
return
|
|
685
|
+
return { width, height };
|
|
435
686
|
}
|
|
436
687
|
/*
|
|
437
688
|
Text nodes with contain 'width' or 'none'
|
|
@@ -446,7 +697,6 @@ function updateDOMTextSize(node) {
|
|
|
446
697
|
if (node.props.h !== size.height) {
|
|
447
698
|
node.props.h = size.height;
|
|
448
699
|
updateNodeStyles(node);
|
|
449
|
-
node.emit('loaded');
|
|
450
700
|
}
|
|
451
701
|
break;
|
|
452
702
|
case 'none':
|
|
@@ -455,10 +705,20 @@ function updateDOMTextSize(node) {
|
|
|
455
705
|
node.props.w = size.width;
|
|
456
706
|
node.props.h = size.height;
|
|
457
707
|
updateNodeStyles(node);
|
|
458
|
-
node.emit('loaded');
|
|
459
708
|
}
|
|
460
709
|
break;
|
|
461
710
|
}
|
|
711
|
+
if (!node.loaded) {
|
|
712
|
+
const payload = {
|
|
713
|
+
type: 'text',
|
|
714
|
+
dimensions: {
|
|
715
|
+
w: node.props.w,
|
|
716
|
+
h: node.props.h,
|
|
717
|
+
},
|
|
718
|
+
};
|
|
719
|
+
node.emit('loaded', payload);
|
|
720
|
+
node.loaded = true;
|
|
721
|
+
}
|
|
462
722
|
}
|
|
463
723
|
function updateDOMTextMeasurements() {
|
|
464
724
|
textNodesToMeasure.forEach(updateDOMTextSize);
|
|
@@ -468,33 +728,36 @@ function scheduleUpdateDOMTextMeasurement(node) {
|
|
|
468
728
|
/*
|
|
469
729
|
Make sure the font is loaded before measuring
|
|
470
730
|
*/
|
|
471
|
-
if (node.fontFamily && !fontFamiliesToLoad.has(node.fontFamily)) {
|
|
472
|
-
fontFamiliesToLoad.add(node.fontFamily);
|
|
473
|
-
document.fonts.load(`16px ${node.fontFamily}`);
|
|
474
|
-
}
|
|
475
731
|
if (textNodesToMeasure.size === 0) {
|
|
732
|
+
const fonts = document.fonts;
|
|
476
733
|
if (document.fonts.status === 'loaded') {
|
|
477
734
|
setTimeout(updateDOMTextMeasurements);
|
|
478
735
|
}
|
|
479
736
|
else {
|
|
480
|
-
|
|
737
|
+
if (fonts && fonts.ready && typeof fonts.ready.then === 'function') {
|
|
738
|
+
fonts.ready.then(updateDOMTextMeasurements);
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
setTimeout(updateDOMTextMeasurements, 500);
|
|
742
|
+
}
|
|
481
743
|
}
|
|
482
744
|
}
|
|
483
745
|
textNodesToMeasure.add(node);
|
|
484
746
|
}
|
|
485
747
|
function updateNodeData(node) {
|
|
486
|
-
|
|
487
|
-
|
|
748
|
+
const data = node.data;
|
|
749
|
+
for (let key in data) {
|
|
750
|
+
let keyValue = data[key];
|
|
488
751
|
if (keyValue === undefined) {
|
|
489
752
|
node.div.removeAttribute('data-' + key);
|
|
490
753
|
}
|
|
491
754
|
else {
|
|
492
|
-
node.div.
|
|
755
|
+
node.div.dataset[key] = String(keyValue);
|
|
493
756
|
}
|
|
494
757
|
}
|
|
495
758
|
}
|
|
496
759
|
function resolveNodeDefaults(props) {
|
|
497
|
-
const color = props.color ??
|
|
760
|
+
const color = props.color ?? 0x00000000;
|
|
498
761
|
return {
|
|
499
762
|
x: props.x ?? 0,
|
|
500
763
|
y: props.y ?? 0,
|
|
@@ -568,14 +831,26 @@ const defaultShader = {
|
|
|
568
831
|
props: undefined,
|
|
569
832
|
};
|
|
570
833
|
let lastNodeId = 0;
|
|
571
|
-
|
|
834
|
+
const CoreNodeRenderStateMap = new Map([
|
|
835
|
+
[0, 'init'],
|
|
836
|
+
[2, 'outOfBounds'],
|
|
837
|
+
[4, 'inBounds'],
|
|
838
|
+
[8, 'inViewport'],
|
|
839
|
+
]);
|
|
840
|
+
export class DOMNode extends EventEmitter {
|
|
572
841
|
stage;
|
|
573
842
|
props;
|
|
574
843
|
div = document.createElement('div');
|
|
575
844
|
divBg;
|
|
576
845
|
divBorder;
|
|
846
|
+
imgEl;
|
|
847
|
+
lazyImagePendingSrc = null;
|
|
848
|
+
lazyImageSubTextureProps = null;
|
|
849
|
+
boundsDirty = true;
|
|
850
|
+
children = new Set();
|
|
577
851
|
id = ++lastNodeId;
|
|
578
852
|
renderState = 0 /* Init */;
|
|
853
|
+
preventCleanup = true;
|
|
579
854
|
constructor(stage, props) {
|
|
580
855
|
super();
|
|
581
856
|
this.stage = stage;
|
|
@@ -584,62 +859,152 @@ class DOMNode extends EventEmitter {
|
|
|
584
859
|
this.div._node = this;
|
|
585
860
|
this.div.setAttribute('data-id', String(this.id));
|
|
586
861
|
elMap.set(this, this.div);
|
|
862
|
+
const parent = this.props.parent;
|
|
863
|
+
if (parent instanceof DOMNode) {
|
|
864
|
+
parent.children.add(this);
|
|
865
|
+
}
|
|
587
866
|
updateNodeParent(this);
|
|
588
867
|
updateNodeStyles(this);
|
|
589
868
|
updateNodeData(this);
|
|
590
869
|
}
|
|
591
870
|
destroy() {
|
|
592
871
|
elMap.delete(this);
|
|
872
|
+
const parent = this.props.parent;
|
|
873
|
+
if (parent instanceof DOMNode) {
|
|
874
|
+
parent.children.delete(this);
|
|
875
|
+
}
|
|
593
876
|
this.div.parentNode.removeChild(this.div);
|
|
594
877
|
}
|
|
595
878
|
get parent() {
|
|
596
879
|
return this.props.parent;
|
|
597
880
|
}
|
|
598
881
|
set parent(value) {
|
|
882
|
+
if (this.props.parent === value)
|
|
883
|
+
return;
|
|
884
|
+
const prevParent = this.props.parent;
|
|
885
|
+
if (prevParent instanceof DOMNode) {
|
|
886
|
+
prevParent.children.delete(this);
|
|
887
|
+
prevParent.markChildrenBoundsDirty();
|
|
888
|
+
}
|
|
599
889
|
this.props.parent = value;
|
|
890
|
+
if (value instanceof DOMNode) {
|
|
891
|
+
value.children.add(this);
|
|
892
|
+
value.markChildrenBoundsDirty();
|
|
893
|
+
}
|
|
894
|
+
this.boundsDirty = true;
|
|
895
|
+
this.markChildrenBoundsDirty();
|
|
600
896
|
updateNodeParent(this);
|
|
601
897
|
}
|
|
898
|
+
markChildrenBoundsDirty() {
|
|
899
|
+
for (const child of this.children) {
|
|
900
|
+
child.boundsDirty = true;
|
|
901
|
+
if (child !== child.stage.root) {
|
|
902
|
+
if (nodeHasTextureSource(child)) {
|
|
903
|
+
const nextState = computeRenderStateForNode(child);
|
|
904
|
+
if (nextState != null) {
|
|
905
|
+
child.updateRenderState(nextState);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
child.boundsDirty = false;
|
|
909
|
+
}
|
|
910
|
+
child.markChildrenBoundsDirty();
|
|
911
|
+
}
|
|
912
|
+
}
|
|
602
913
|
animate = animate;
|
|
914
|
+
updateRenderState(renderState) {
|
|
915
|
+
if (renderState === this.renderState)
|
|
916
|
+
return;
|
|
917
|
+
const previous = this.renderState;
|
|
918
|
+
this.renderState = renderState;
|
|
919
|
+
const event = CoreNodeRenderStateMap.get(renderState);
|
|
920
|
+
if (isRenderStateInBounds(renderState)) {
|
|
921
|
+
this.applyPendingImageSrc();
|
|
922
|
+
}
|
|
923
|
+
if (event && event !== 'init') {
|
|
924
|
+
this.emit(event, { previous, current: renderState });
|
|
925
|
+
}
|
|
926
|
+
if (this.imgEl) {
|
|
927
|
+
this.imgEl.dataset.state = event;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
applyPendingImageSrc() {
|
|
931
|
+
if (!this.imgEl)
|
|
932
|
+
return;
|
|
933
|
+
const pendingSrc = this.lazyImagePendingSrc;
|
|
934
|
+
if (!pendingSrc)
|
|
935
|
+
return;
|
|
936
|
+
if (this.imgEl.dataset.rawSrc === pendingSrc)
|
|
937
|
+
return;
|
|
938
|
+
this.imgEl.style.display = '';
|
|
939
|
+
this.imgEl.dataset.pendingSrc = pendingSrc;
|
|
940
|
+
this.imgEl.src = pendingSrc;
|
|
941
|
+
this.imgEl.dataset.rawSrc = pendingSrc;
|
|
942
|
+
this.imgEl.dataset.pendingSrc = '';
|
|
943
|
+
}
|
|
603
944
|
get x() {
|
|
604
945
|
return this.props.x;
|
|
605
946
|
}
|
|
606
947
|
set x(v) {
|
|
948
|
+
if (this.props.x === v)
|
|
949
|
+
return;
|
|
607
950
|
this.props.x = v;
|
|
951
|
+
this.boundsDirty = true;
|
|
952
|
+
this.markChildrenBoundsDirty();
|
|
608
953
|
updateNodeStyles(this);
|
|
609
954
|
}
|
|
610
955
|
get y() {
|
|
611
956
|
return this.props.y;
|
|
612
957
|
}
|
|
613
958
|
set y(v) {
|
|
959
|
+
if (this.props.y === v)
|
|
960
|
+
return;
|
|
614
961
|
this.props.y = v;
|
|
962
|
+
this.boundsDirty = true;
|
|
963
|
+
this.markChildrenBoundsDirty();
|
|
615
964
|
updateNodeStyles(this);
|
|
616
965
|
}
|
|
617
966
|
get w() {
|
|
618
967
|
return this.props.w;
|
|
619
968
|
}
|
|
620
969
|
set w(v) {
|
|
970
|
+
if (this.props.w === v)
|
|
971
|
+
return;
|
|
621
972
|
this.props.w = v;
|
|
973
|
+
this.boundsDirty = true;
|
|
974
|
+
this.markChildrenBoundsDirty();
|
|
622
975
|
updateNodeStyles(this);
|
|
623
976
|
}
|
|
624
977
|
get h() {
|
|
625
978
|
return this.props.h;
|
|
626
979
|
}
|
|
627
980
|
set h(v) {
|
|
981
|
+
if (this.props.h === v)
|
|
982
|
+
return;
|
|
628
983
|
this.props.h = v;
|
|
984
|
+
this.boundsDirty = true;
|
|
985
|
+
this.markChildrenBoundsDirty();
|
|
629
986
|
updateNodeStyles(this);
|
|
630
987
|
}
|
|
631
988
|
get width() {
|
|
632
989
|
return this.props.w;
|
|
633
990
|
}
|
|
634
991
|
set width(v) {
|
|
992
|
+
if (this.props.w === v)
|
|
993
|
+
return;
|
|
635
994
|
this.props.w = v;
|
|
995
|
+
this.boundsDirty = true;
|
|
996
|
+
this.markChildrenBoundsDirty();
|
|
636
997
|
updateNodeStyles(this);
|
|
637
998
|
}
|
|
638
999
|
get height() {
|
|
639
1000
|
return this.props.h;
|
|
640
1001
|
}
|
|
641
1002
|
set height(v) {
|
|
1003
|
+
if (this.props.h === v)
|
|
1004
|
+
return;
|
|
642
1005
|
this.props.h = v;
|
|
1006
|
+
this.boundsDirty = true;
|
|
1007
|
+
this.markChildrenBoundsDirty();
|
|
643
1008
|
updateNodeStyles(this);
|
|
644
1009
|
}
|
|
645
1010
|
get alpha() {
|
|
@@ -730,14 +1095,19 @@ class DOMNode extends EventEmitter {
|
|
|
730
1095
|
return this.props.zIndex;
|
|
731
1096
|
}
|
|
732
1097
|
set zIndex(v) {
|
|
733
|
-
this.props.zIndex
|
|
1098
|
+
if (this.props.zIndex === v)
|
|
1099
|
+
return;
|
|
1100
|
+
this.props.zIndex = Math.ceil(v);
|
|
734
1101
|
updateNodeStyles(this);
|
|
735
1102
|
}
|
|
736
1103
|
get texture() {
|
|
737
1104
|
return this.props.texture;
|
|
738
1105
|
}
|
|
739
1106
|
set texture(v) {
|
|
1107
|
+
if (this.props.texture === v)
|
|
1108
|
+
return;
|
|
740
1109
|
this.props.texture = v;
|
|
1110
|
+
this.boundsDirty = true;
|
|
741
1111
|
updateNodeStyles(this);
|
|
742
1112
|
}
|
|
743
1113
|
get textureOptions() {
|
|
@@ -751,13 +1121,18 @@ class DOMNode extends EventEmitter {
|
|
|
751
1121
|
return this.props.src;
|
|
752
1122
|
}
|
|
753
1123
|
set src(v) {
|
|
1124
|
+
if (this.props.src === v)
|
|
1125
|
+
return;
|
|
754
1126
|
this.props.src = v;
|
|
1127
|
+
this.boundsDirty = true;
|
|
755
1128
|
updateNodeStyles(this);
|
|
756
1129
|
}
|
|
757
1130
|
get scale() {
|
|
758
1131
|
return this.props.scale ?? 1;
|
|
759
1132
|
}
|
|
760
1133
|
set scale(v) {
|
|
1134
|
+
if (this.props.scale === v)
|
|
1135
|
+
return;
|
|
761
1136
|
this.props.scale = v;
|
|
762
1137
|
updateNodeStyles(this);
|
|
763
1138
|
}
|
|
@@ -880,25 +1255,37 @@ class DOMNode extends EventEmitter {
|
|
|
880
1255
|
}
|
|
881
1256
|
set boundsMargin(value) {
|
|
882
1257
|
this.props.boundsMargin = value;
|
|
1258
|
+
this.boundsDirty = true;
|
|
1259
|
+
this.markChildrenBoundsDirty();
|
|
883
1260
|
}
|
|
884
1261
|
get absX() {
|
|
885
|
-
|
|
1262
|
+
const parent = this.props.parent;
|
|
1263
|
+
return (this.x +
|
|
1264
|
+
-this.w * this.mountX +
|
|
1265
|
+
(parent instanceof DOMNode ? parent.absX : 0));
|
|
886
1266
|
}
|
|
887
1267
|
get absY() {
|
|
888
|
-
|
|
1268
|
+
const parent = this.props.parent;
|
|
1269
|
+
return (this.y +
|
|
1270
|
+
-this.h * this.mountY +
|
|
1271
|
+
(parent instanceof DOMNode ? parent.absY : 0));
|
|
889
1272
|
}
|
|
890
1273
|
}
|
|
891
1274
|
class DOMText extends DOMNode {
|
|
892
1275
|
props;
|
|
1276
|
+
loaded = false;
|
|
893
1277
|
constructor(stage, props) {
|
|
894
1278
|
super(stage, props);
|
|
895
1279
|
this.props = props;
|
|
896
1280
|
this.div.innerText = props.text;
|
|
1281
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
897
1282
|
}
|
|
898
1283
|
get text() {
|
|
899
1284
|
return this.props.text;
|
|
900
1285
|
}
|
|
901
1286
|
set text(v) {
|
|
1287
|
+
if (this.props.text === v)
|
|
1288
|
+
return;
|
|
902
1289
|
this.props.text = v;
|
|
903
1290
|
this.div.innerText = v;
|
|
904
1291
|
scheduleUpdateDOMTextMeasurement(this);
|
|
@@ -907,29 +1294,51 @@ class DOMText extends DOMNode {
|
|
|
907
1294
|
return this.props.fontFamily;
|
|
908
1295
|
}
|
|
909
1296
|
set fontFamily(v) {
|
|
1297
|
+
if (this.props.fontFamily === v)
|
|
1298
|
+
return;
|
|
910
1299
|
this.props.fontFamily = v;
|
|
911
1300
|
updateNodeStyles(this);
|
|
1301
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
912
1302
|
}
|
|
913
1303
|
get fontSize() {
|
|
914
1304
|
return this.props.fontSize;
|
|
915
1305
|
}
|
|
916
1306
|
set fontSize(v) {
|
|
1307
|
+
if (this.props.fontSize === v)
|
|
1308
|
+
return;
|
|
917
1309
|
this.props.fontSize = v;
|
|
918
1310
|
updateNodeStyles(this);
|
|
1311
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
919
1312
|
}
|
|
920
1313
|
get fontStyle() {
|
|
921
1314
|
return this.props.fontStyle;
|
|
922
1315
|
}
|
|
923
1316
|
set fontStyle(v) {
|
|
1317
|
+
if (this.props.fontStyle === v)
|
|
1318
|
+
return;
|
|
924
1319
|
this.props.fontStyle = v;
|
|
925
1320
|
updateNodeStyles(this);
|
|
1321
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
926
1322
|
}
|
|
927
1323
|
get fontWeight() {
|
|
928
1324
|
return this.props.fontWeight;
|
|
929
1325
|
}
|
|
930
1326
|
set fontWeight(v) {
|
|
1327
|
+
if (this.props.fontWeight === v)
|
|
1328
|
+
return;
|
|
931
1329
|
this.props.fontWeight = v;
|
|
932
1330
|
updateNodeStyles(this);
|
|
1331
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1332
|
+
}
|
|
1333
|
+
get fontStretch() {
|
|
1334
|
+
return this.props.fontStretch;
|
|
1335
|
+
}
|
|
1336
|
+
set fontStretch(v) {
|
|
1337
|
+
if (this.props.fontStretch === v)
|
|
1338
|
+
return;
|
|
1339
|
+
this.props.fontStretch = v;
|
|
1340
|
+
updateNodeStyles(this);
|
|
1341
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
933
1342
|
}
|
|
934
1343
|
get forceLoad() {
|
|
935
1344
|
return this.props.forceLoad;
|
|
@@ -941,34 +1350,48 @@ class DOMText extends DOMNode {
|
|
|
941
1350
|
return this.props.lineHeight;
|
|
942
1351
|
}
|
|
943
1352
|
set lineHeight(v) {
|
|
1353
|
+
if (this.props.lineHeight === v)
|
|
1354
|
+
return;
|
|
944
1355
|
this.props.lineHeight = v;
|
|
945
1356
|
updateNodeStyles(this);
|
|
1357
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
946
1358
|
}
|
|
947
1359
|
get maxWidth() {
|
|
948
1360
|
return this.props.maxWidth;
|
|
949
1361
|
}
|
|
950
1362
|
set maxWidth(v) {
|
|
1363
|
+
if (this.props.maxWidth === v)
|
|
1364
|
+
return;
|
|
951
1365
|
this.props.maxWidth = v;
|
|
952
1366
|
updateNodeStyles(this);
|
|
1367
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
953
1368
|
}
|
|
954
1369
|
get maxHeight() {
|
|
955
1370
|
return this.props.maxHeight;
|
|
956
1371
|
}
|
|
957
1372
|
set maxHeight(v) {
|
|
1373
|
+
if (this.props.maxHeight === v)
|
|
1374
|
+
return;
|
|
958
1375
|
this.props.maxHeight = v;
|
|
959
1376
|
updateNodeStyles(this);
|
|
1377
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
960
1378
|
}
|
|
961
1379
|
get letterSpacing() {
|
|
962
1380
|
return this.props.letterSpacing;
|
|
963
1381
|
}
|
|
964
1382
|
set letterSpacing(v) {
|
|
1383
|
+
if (this.props.letterSpacing === v)
|
|
1384
|
+
return;
|
|
965
1385
|
this.props.letterSpacing = v;
|
|
966
1386
|
updateNodeStyles(this);
|
|
1387
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
967
1388
|
}
|
|
968
1389
|
get textAlign() {
|
|
969
1390
|
return this.props.textAlign;
|
|
970
1391
|
}
|
|
971
1392
|
set textAlign(v) {
|
|
1393
|
+
if (this.props.textAlign === v)
|
|
1394
|
+
return;
|
|
972
1395
|
this.props.textAlign = v;
|
|
973
1396
|
updateNodeStyles(this);
|
|
974
1397
|
}
|
|
@@ -976,6 +1399,8 @@ class DOMText extends DOMNode {
|
|
|
976
1399
|
return this.props.overflowSuffix;
|
|
977
1400
|
}
|
|
978
1401
|
set overflowSuffix(v) {
|
|
1402
|
+
if (this.props.overflowSuffix === v)
|
|
1403
|
+
return;
|
|
979
1404
|
this.props.overflowSuffix = v;
|
|
980
1405
|
updateNodeStyles(this);
|
|
981
1406
|
}
|
|
@@ -983,15 +1408,21 @@ class DOMText extends DOMNode {
|
|
|
983
1408
|
return this.props.maxLines;
|
|
984
1409
|
}
|
|
985
1410
|
set maxLines(v) {
|
|
1411
|
+
if (this.props.maxLines === v)
|
|
1412
|
+
return;
|
|
986
1413
|
this.props.maxLines = v;
|
|
987
1414
|
updateNodeStyles(this);
|
|
1415
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
988
1416
|
}
|
|
989
1417
|
get contain() {
|
|
990
1418
|
return this.props.contain;
|
|
991
1419
|
}
|
|
992
1420
|
set contain(v) {
|
|
1421
|
+
if (this.props.contain === v)
|
|
1422
|
+
return;
|
|
993
1423
|
this.props.contain = v;
|
|
994
1424
|
updateNodeStyles(this);
|
|
1425
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
995
1426
|
}
|
|
996
1427
|
get verticalAlign() {
|
|
997
1428
|
return this.props.verticalAlign;
|
|
@@ -1044,6 +1475,7 @@ export class DOMRendererMain {
|
|
|
1044
1475
|
root;
|
|
1045
1476
|
canvas;
|
|
1046
1477
|
stage;
|
|
1478
|
+
eventListeners = new Map();
|
|
1047
1479
|
constructor(settings, rawTarget) {
|
|
1048
1480
|
this.settings = settings;
|
|
1049
1481
|
let target;
|
|
@@ -1070,22 +1502,27 @@ export class DOMRendererMain {
|
|
|
1070
1502
|
root: null,
|
|
1071
1503
|
renderer: {
|
|
1072
1504
|
mode: 'canvas',
|
|
1505
|
+
boundsMargin: settings.boundsMargin,
|
|
1073
1506
|
},
|
|
1074
|
-
loadFont: async () => { },
|
|
1075
1507
|
shManager: {
|
|
1076
1508
|
registerShaderType() { },
|
|
1077
1509
|
},
|
|
1078
1510
|
animationManager: {
|
|
1079
|
-
registerAnimation() {
|
|
1080
|
-
|
|
1511
|
+
registerAnimation(anim) {
|
|
1512
|
+
console.log('registerAnimation', anim);
|
|
1513
|
+
},
|
|
1514
|
+
unregisterAnimation(anim) {
|
|
1515
|
+
console.log('unregisterAnimation', anim);
|
|
1516
|
+
},
|
|
1081
1517
|
},
|
|
1518
|
+
loadFont: async () => { },
|
|
1082
1519
|
cleanup() { },
|
|
1083
1520
|
};
|
|
1084
1521
|
this.root = new DOMNode(this.stage, resolveNodeDefaults({
|
|
1085
1522
|
w: settings.appWidth ?? 1920,
|
|
1086
1523
|
h: settings.appHeight ?? 1080,
|
|
1087
1524
|
shader: defaultShader,
|
|
1088
|
-
zIndex:
|
|
1525
|
+
zIndex: 1,
|
|
1089
1526
|
}));
|
|
1090
1527
|
this.stage.root = this.root;
|
|
1091
1528
|
target.appendChild(this.root.div);
|
|
@@ -1116,14 +1553,76 @@ export class DOMRendererMain {
|
|
|
1116
1553
|
new ResizeObserver(updateRootPosition.bind(this)).observe(this.canvas);
|
|
1117
1554
|
window.addEventListener('resize', updateRootPosition.bind(this));
|
|
1118
1555
|
}
|
|
1556
|
+
removeAllListeners() {
|
|
1557
|
+
if (this.eventListeners.size === 0)
|
|
1558
|
+
return;
|
|
1559
|
+
this.eventListeners.forEach((listeners) => listeners.clear());
|
|
1560
|
+
this.eventListeners.clear();
|
|
1561
|
+
}
|
|
1562
|
+
once(event, listener) {
|
|
1563
|
+
const wrappedListener = (target, data) => {
|
|
1564
|
+
this.off(event, wrappedListener);
|
|
1565
|
+
listener(target, data);
|
|
1566
|
+
};
|
|
1567
|
+
this.on(event, wrappedListener);
|
|
1568
|
+
}
|
|
1569
|
+
on(name, callback) {
|
|
1570
|
+
let listeners = this.eventListeners.get(name);
|
|
1571
|
+
if (!listeners) {
|
|
1572
|
+
listeners = new Set();
|
|
1573
|
+
this.eventListeners.set(name, listeners);
|
|
1574
|
+
}
|
|
1575
|
+
listeners.add(callback);
|
|
1576
|
+
}
|
|
1577
|
+
off(event, listener) {
|
|
1578
|
+
const listeners = this.eventListeners.get(event);
|
|
1579
|
+
if (listeners) {
|
|
1580
|
+
listeners.delete(listener);
|
|
1581
|
+
if (listeners.size === 0) {
|
|
1582
|
+
this.eventListeners.delete(event);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
emit(event, targetOrData, maybeData) {
|
|
1587
|
+
const listeners = this.eventListeners.get(event);
|
|
1588
|
+
if (!listeners || listeners.size === 0) {
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
const hasExplicitTarget = arguments.length === 3;
|
|
1592
|
+
const target = hasExplicitTarget ? targetOrData : this.root;
|
|
1593
|
+
const data = hasExplicitTarget ? maybeData : targetOrData;
|
|
1594
|
+
for (const listener of Array.from(listeners)) {
|
|
1595
|
+
try {
|
|
1596
|
+
listener(target, data);
|
|
1597
|
+
}
|
|
1598
|
+
catch (error) {
|
|
1599
|
+
console.error(`Error in listener for event "${event}"`, error);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1119
1603
|
createNode(props) {
|
|
1120
1604
|
return new DOMNode(this.stage, resolveNodeDefaults(props));
|
|
1121
1605
|
}
|
|
1122
1606
|
createTextNode(props) {
|
|
1123
1607
|
return new DOMText(this.stage, resolveTextNodeDefaults(props));
|
|
1124
1608
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1609
|
+
/** TODO: restore this */
|
|
1610
|
+
// createShader<ShType extends keyof ShaderMap>(
|
|
1611
|
+
// shType: ShType,
|
|
1612
|
+
// props?: OptionalShaderProps<ShType>,
|
|
1613
|
+
// ): InstanceType<lng.ShaderMap[ShType]> {
|
|
1614
|
+
// return { shaderType: shType, props, program: {} } as InstanceType<
|
|
1615
|
+
// lng.ShaderMap[ShType]
|
|
1616
|
+
// >;
|
|
1617
|
+
// }
|
|
1618
|
+
createShader(...args) {
|
|
1619
|
+
const [shaderType, props] = args;
|
|
1620
|
+
return {
|
|
1621
|
+
// @ts-ignore
|
|
1622
|
+
shaderType,
|
|
1623
|
+
props,
|
|
1624
|
+
program: {},
|
|
1625
|
+
};
|
|
1127
1626
|
}
|
|
1128
1627
|
createTexture(textureType, props) {
|
|
1129
1628
|
let type = lng.TextureType.generic;
|
|
@@ -1146,8 +1645,20 @@ export class DOMRendererMain {
|
|
|
1146
1645
|
}
|
|
1147
1646
|
return { type, props };
|
|
1148
1647
|
}
|
|
1149
|
-
|
|
1150
|
-
|
|
1648
|
+
}
|
|
1649
|
+
export function loadFontToDom(font) {
|
|
1650
|
+
// fontFamily: string;
|
|
1651
|
+
// metrics?: FontMetrics;
|
|
1652
|
+
// fontUrl?: string;
|
|
1653
|
+
// atlasUrl?: string;
|
|
1654
|
+
// atlasDataUrl?: string;
|
|
1655
|
+
const fontFace = new FontFace(font.fontFamily, `url(${font.fontUrl})`);
|
|
1656
|
+
if (typeof document !== 'undefined' && 'fonts' in document) {
|
|
1657
|
+
const fontSet = document.fonts;
|
|
1658
|
+
fontSet.add?.(fontFace);
|
|
1151
1659
|
}
|
|
1152
1660
|
}
|
|
1661
|
+
export function isDomRenderer(r) {
|
|
1662
|
+
return r instanceof DOMRendererMain;
|
|
1663
|
+
}
|
|
1153
1664
|
//# sourceMappingURL=domRenderer.js.map
|