@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,72 +5,45 @@ Experimental DOM renderer
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as lng from '@lightningjs/renderer';
|
|
8
|
-
import { EventEmitter } from '@lightningjs/renderer/utils';
|
|
9
8
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
IRendererShaderProps,
|
|
15
|
-
IRendererTextureProps,
|
|
16
|
-
IRendererTexture,
|
|
9
|
+
import { EventEmitter } from '@lightningjs/renderer/utils';
|
|
10
|
+
import { Config } from '../config.js';
|
|
11
|
+
import type {
|
|
12
|
+
ExtractProps,
|
|
17
13
|
IRendererMain,
|
|
18
14
|
IRendererNode,
|
|
19
15
|
IRendererNodeProps,
|
|
16
|
+
IRendererShader,
|
|
17
|
+
IRendererStage,
|
|
20
18
|
IRendererTextNode,
|
|
21
19
|
IRendererTextNodeProps,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
}
|
|
20
|
+
DomRendererMainSettings,
|
|
21
|
+
} from './domRendererTypes.js';
|
|
22
|
+
import {
|
|
23
|
+
colorToRgba,
|
|
24
|
+
buildGradientStops,
|
|
25
|
+
computeLegacyObjectFit,
|
|
26
|
+
applySubTextureScaling,
|
|
27
|
+
getNodeLineHeight,
|
|
28
|
+
applyEasing,
|
|
29
|
+
interpolateProp,
|
|
30
|
+
isRenderStateInBounds,
|
|
31
|
+
nodeHasTextureSource,
|
|
32
|
+
computeRenderStateForNode,
|
|
33
|
+
compactString,
|
|
34
|
+
} from './domRendererUtils.js';
|
|
35
|
+
import { FontLoadOptions } from '../intrinsicTypes.js';
|
|
36
|
+
|
|
37
|
+
// Feature detection for legacy brousers
|
|
38
|
+
const _styleRef: any =
|
|
39
|
+
typeof document !== 'undefined' ? document.documentElement?.style || {} : {};
|
|
40
|
+
|
|
41
|
+
const supportsObjectFit: boolean = 'objectFit' in _styleRef;
|
|
42
|
+
const supportsObjectPosition: boolean = 'objectPosition' in _styleRef;
|
|
43
|
+
const supportsMixBlendMode: boolean = 'mixBlendMode' in _styleRef;
|
|
44
|
+
const supportsStandardMask: boolean = 'maskImage' in _styleRef;
|
|
45
|
+
const supportsWebkitMask: boolean = 'webkitMaskImage' in _styleRef;
|
|
46
|
+
const supportsCssMask: boolean = supportsStandardMask || supportsWebkitMask;
|
|
74
47
|
|
|
75
48
|
/*
|
|
76
49
|
Animations
|
|
@@ -117,6 +90,8 @@ function updateAnimations(time: number) {
|
|
|
117
90
|
// Animation complete
|
|
118
91
|
else {
|
|
119
92
|
Object.assign(task.node.props, task.propsEnd);
|
|
93
|
+
task.node.boundsDirty = true;
|
|
94
|
+
task.node.markChildrenBoundsDirty();
|
|
120
95
|
updateNodeStyles(task.node);
|
|
121
96
|
|
|
122
97
|
task.stop();
|
|
@@ -251,17 +226,12 @@ function animate(
|
|
|
251
226
|
let elMap = new WeakMap<DOMNode, HTMLElement>();
|
|
252
227
|
|
|
253
228
|
function updateNodeParent(node: DOMNode | DOMText) {
|
|
254
|
-
|
|
255
|
-
|
|
229
|
+
const parent = node.props.parent;
|
|
230
|
+
if (parent instanceof DOMNode) {
|
|
231
|
+
elMap.get(parent)!.appendChild(node.div);
|
|
256
232
|
}
|
|
257
233
|
}
|
|
258
234
|
|
|
259
|
-
function getNodeLineHeight(props: IRendererTextNodeProps): number {
|
|
260
|
-
return (
|
|
261
|
-
props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
235
|
function updateNodeStyles(node: DOMNode | DOMText) {
|
|
266
236
|
let { props } = node;
|
|
267
237
|
|
|
@@ -324,6 +294,9 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
324
294
|
if (textProps.fontWeight !== 'normal') {
|
|
325
295
|
style += `font-weight: ${textProps.fontWeight};`;
|
|
326
296
|
}
|
|
297
|
+
if (textProps.fontStretch && textProps.fontStretch !== 'normal') {
|
|
298
|
+
style += `font-stretch: ${textProps.fontStretch};`;
|
|
299
|
+
}
|
|
327
300
|
if (textProps.lineHeight != null) {
|
|
328
301
|
style += `line-height: ${textProps.lineHeight}px;`;
|
|
329
302
|
}
|
|
@@ -337,21 +310,45 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
337
310
|
let maxLines = textProps.maxLines || Infinity;
|
|
338
311
|
switch (textProps.contain) {
|
|
339
312
|
case 'width':
|
|
340
|
-
|
|
313
|
+
if (textProps.maxWidth && textProps.maxWidth > 0) {
|
|
314
|
+
style += `width: ${textProps.maxWidth}px;`;
|
|
315
|
+
} else {
|
|
316
|
+
style += `width: 100%;`;
|
|
317
|
+
}
|
|
318
|
+
style += `overflow: hidden;`;
|
|
341
319
|
break;
|
|
342
320
|
case 'both': {
|
|
343
321
|
let lineHeight = getNodeLineHeight(textProps);
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
322
|
+
const widthConstraint =
|
|
323
|
+
textProps.maxWidth && textProps.maxWidth > 0
|
|
324
|
+
? `${textProps.maxWidth}px`
|
|
325
|
+
: `100%`;
|
|
326
|
+
const heightConstraint =
|
|
327
|
+
textProps.maxHeight && textProps.maxHeight > 0
|
|
328
|
+
? textProps.maxHeight
|
|
329
|
+
: props.h;
|
|
330
|
+
|
|
331
|
+
let height = heightConstraint || 0;
|
|
332
|
+
if (height > 0) {
|
|
333
|
+
const maxLinesByHeight = Math.max(1, Math.floor(height / lineHeight));
|
|
334
|
+
maxLines = Math.min(maxLines, maxLinesByHeight);
|
|
335
|
+
height = Math.max(lineHeight, maxLines * lineHeight);
|
|
336
|
+
} else {
|
|
337
|
+
maxLines = Number.isFinite(maxLines) ? Math.max(1, maxLines) : 1;
|
|
338
|
+
height = maxLines * lineHeight;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
style += `width: ${widthConstraint}; height: ${height}px; overflow: hidden;`;
|
|
348
342
|
break;
|
|
349
343
|
}
|
|
350
344
|
case 'none':
|
|
345
|
+
style += `width: -webkit-max-content;`;
|
|
351
346
|
style += `width: max-content;`;
|
|
352
347
|
break;
|
|
353
348
|
}
|
|
354
349
|
|
|
350
|
+
style += `white-space: pre-wrap;`;
|
|
351
|
+
|
|
355
352
|
if (maxLines !== Infinity) {
|
|
356
353
|
// https://stackoverflow.com/a/13924997
|
|
357
354
|
style += `display: -webkit-box;
|
|
@@ -363,12 +360,10 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
363
360
|
|
|
364
361
|
// if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};`
|
|
365
362
|
// if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};`
|
|
366
|
-
|
|
367
|
-
scheduleUpdateDOMTextMeasurement(node);
|
|
368
363
|
}
|
|
369
364
|
// <Node>
|
|
370
365
|
else {
|
|
371
|
-
if (props.w !== 0) style += `width: ${props.w}px;`;
|
|
366
|
+
if (props.w !== 0) style += `width: ${props.w < 0 ? 0 : props.w}px;`;
|
|
372
367
|
if (props.h !== 0) style += `height: ${props.h}px;`;
|
|
373
368
|
|
|
374
369
|
let vGradient =
|
|
@@ -387,57 +382,101 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
387
382
|
: vGradient || hGradient;
|
|
388
383
|
|
|
389
384
|
let srcImg: string | null = null;
|
|
390
|
-
let srcPos: null |
|
|
385
|
+
let srcPos: null | InstanceType<lng.TextureMap['SubTexture']>['props'] =
|
|
386
|
+
null;
|
|
387
|
+
let rawImgSrc: string | null = null;
|
|
391
388
|
|
|
392
389
|
if (
|
|
393
390
|
props.texture != null &&
|
|
394
391
|
props.texture.type === lng.TextureType.subTexture
|
|
395
392
|
) {
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
const texture = props.texture as InstanceType<
|
|
394
|
+
lng.TextureMap['SubTexture']
|
|
395
|
+
>;
|
|
396
|
+
srcPos = texture.props;
|
|
397
|
+
rawImgSrc = (texture.props.texture as any).props.src;
|
|
398
398
|
} else if (props.src) {
|
|
399
|
-
|
|
399
|
+
rawImgSrc = props.src;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (rawImgSrc) {
|
|
403
|
+
srcImg = `url(${rawImgSrc})`;
|
|
400
404
|
}
|
|
401
405
|
|
|
402
406
|
let bgStyle = '';
|
|
403
407
|
let borderStyle = '';
|
|
404
408
|
let radiusStyle = '';
|
|
405
409
|
let maskStyle = '';
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
410
|
+
let needsBackgroundLayer = false;
|
|
411
|
+
let imgStyle = '';
|
|
412
|
+
let hasDivBgTint = false;
|
|
413
|
+
|
|
414
|
+
if (rawImgSrc) {
|
|
415
|
+
needsBackgroundLayer = true;
|
|
416
|
+
|
|
417
|
+
const hasTint = props.color !== 0xffffffff && props.color !== 0x00000000;
|
|
418
|
+
|
|
419
|
+
if (hasTint) {
|
|
420
|
+
bgStyle += `background-color: ${colorToRgba(props.color)};`;
|
|
421
|
+
if (srcImg) {
|
|
422
|
+
maskStyle += `mask-image: ${srcImg};`;
|
|
423
|
+
if (srcPos !== null) {
|
|
424
|
+
maskStyle += `mask-position: -${srcPos.x}px -${srcPos.y}px;`;
|
|
425
|
+
} else {
|
|
426
|
+
maskStyle += `mask-size: 100% 100%;`;
|
|
427
|
+
}
|
|
428
|
+
hasDivBgTint = true;
|
|
416
429
|
}
|
|
417
430
|
} else if (gradient) {
|
|
418
|
-
// use gradient as a mask
|
|
431
|
+
// use gradient as a mask when no tint is applied
|
|
419
432
|
maskStyle += `mask-image: ${gradient};`;
|
|
420
433
|
}
|
|
421
434
|
|
|
422
|
-
|
|
423
|
-
|
|
435
|
+
const imgStyleParts = [
|
|
436
|
+
'position: absolute',
|
|
437
|
+
'top: 0',
|
|
438
|
+
'left: 0',
|
|
439
|
+
'right: 0',
|
|
440
|
+
'bottom: 0',
|
|
441
|
+
'display: block',
|
|
442
|
+
'pointer-events: none',
|
|
443
|
+
];
|
|
424
444
|
|
|
425
445
|
if (props.textureOptions.resizeMode?.type) {
|
|
426
|
-
|
|
446
|
+
const resizeMode = props.textureOptions.resizeMode;
|
|
447
|
+
imgStyleParts.push('width: 100%');
|
|
448
|
+
imgStyleParts.push('height: 100%');
|
|
449
|
+
imgStyleParts.push(`object-fit: ${resizeMode.type}`);
|
|
450
|
+
|
|
451
|
+
// Handle clipX and clipY for object-position
|
|
452
|
+
const clipX = (resizeMode as any).clipX ?? 0.5;
|
|
453
|
+
const clipY = (resizeMode as any).clipY ?? 0.5;
|
|
454
|
+
imgStyleParts.push(`object-position: ${clipX * 100}% ${clipY * 100}%`);
|
|
427
455
|
} else if (srcPos !== null) {
|
|
428
|
-
|
|
456
|
+
imgStyleParts.push('width: auto');
|
|
457
|
+
imgStyleParts.push('height: auto');
|
|
458
|
+
imgStyleParts.push('object-fit: none');
|
|
459
|
+
imgStyleParts.push(`object-position: -${srcPos.x}px -${srcPos.y}px`);
|
|
460
|
+
} else if (props.w && !props.h) {
|
|
461
|
+
imgStyleParts.push('width: 100%');
|
|
462
|
+
imgStyleParts.push('height: auto');
|
|
463
|
+
} else if (props.h && !props.w) {
|
|
464
|
+
imgStyleParts.push('width: auto');
|
|
465
|
+
imgStyleParts.push('height: 100%');
|
|
429
466
|
} else {
|
|
430
|
-
|
|
467
|
+
imgStyleParts.push('width: 100%');
|
|
468
|
+
imgStyleParts.push('height: 100%');
|
|
469
|
+
imgStyleParts.push('object-fit: fill');
|
|
431
470
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
node.div.appendChild((node.divBg = document.createElement('div')));
|
|
439
|
-
node.div.appendChild((node.divBorder = document.createElement('div')));
|
|
471
|
+
if (hasTint) {
|
|
472
|
+
if (supportsMixBlendMode) {
|
|
473
|
+
imgStyleParts.push('mix-blend-mode: multiply');
|
|
474
|
+
} else {
|
|
475
|
+
imgStyleParts.push('opacity: 0.9');
|
|
476
|
+
}
|
|
440
477
|
}
|
|
478
|
+
|
|
479
|
+
imgStyle = imgStyleParts.join('; ') + ';';
|
|
441
480
|
} else if (gradient) {
|
|
442
481
|
bgStyle += `background-image: ${gradient};`;
|
|
443
482
|
bgStyle += `background-repeat: no-repeat;`;
|
|
@@ -447,13 +486,13 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
447
486
|
}
|
|
448
487
|
|
|
449
488
|
if (props.shader?.props != null) {
|
|
450
|
-
let
|
|
489
|
+
let shaderProps = props.shader.props;
|
|
451
490
|
|
|
452
|
-
let borderWidth =
|
|
453
|
-
let borderColor =
|
|
454
|
-
let borderGap =
|
|
455
|
-
let borderInset =
|
|
456
|
-
let radius =
|
|
491
|
+
let borderWidth = shaderProps['border-w'];
|
|
492
|
+
let borderColor = shaderProps['border-color'];
|
|
493
|
+
let borderGap = shaderProps['border-gap'] ?? 0;
|
|
494
|
+
let borderInset = shaderProps['border-inset'] ?? true;
|
|
495
|
+
let radius = shaderProps['radius'];
|
|
457
496
|
|
|
458
497
|
// Border
|
|
459
498
|
if (
|
|
@@ -462,10 +501,10 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
462
501
|
typeof borderColor === 'number' &&
|
|
463
502
|
borderColor !== 0
|
|
464
503
|
) {
|
|
504
|
+
const rgbaColor = colorToRgba(borderColor);
|
|
465
505
|
// Handle inset borders by making gap negative
|
|
466
506
|
let gap = borderInset ? -(borderWidth + borderGap) : borderGap;
|
|
467
|
-
|
|
468
|
-
borderStyle += `outline: ${borderWidth}px solid ${colorToRgba(borderColor)};`;
|
|
507
|
+
borderStyle += `outline: ${borderWidth}px solid ${rgbaColor};`;
|
|
469
508
|
borderStyle += `outline-offset: ${gap}px;`;
|
|
470
509
|
}
|
|
471
510
|
// Rounded
|
|
@@ -474,49 +513,302 @@ function updateNodeStyles(node: DOMNode | DOMText) {
|
|
|
474
513
|
} else if (Array.isArray(radius) && radius.length === 4) {
|
|
475
514
|
radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`;
|
|
476
515
|
}
|
|
516
|
+
|
|
517
|
+
if ('radial' in shaderProps) {
|
|
518
|
+
const rg = shaderProps.radial as
|
|
519
|
+
| Partial<lng.RadialGradientProps>
|
|
520
|
+
| undefined;
|
|
521
|
+
const colors = Array.isArray(rg?.colors) ? rg!.colors! : [];
|
|
522
|
+
const stops = Array.isArray(rg?.stops) ? rg!.stops! : undefined;
|
|
523
|
+
const pivot = Array.isArray(rg?.pivot) ? rg!.pivot! : [0.5, 0.5];
|
|
524
|
+
const width = typeof rg?.w === 'number' ? rg!.w! : props.w || 0;
|
|
525
|
+
const height = typeof rg?.h === 'number' ? rg!.h! : width;
|
|
526
|
+
|
|
527
|
+
if (colors.length > 0) {
|
|
528
|
+
const gradientStops = buildGradientStops(colors, stops);
|
|
529
|
+
if (gradientStops) {
|
|
530
|
+
if (colors.length === 1) {
|
|
531
|
+
// Single color -> solid fill
|
|
532
|
+
if (srcImg || gradient) {
|
|
533
|
+
maskStyle += `mask-image: linear-gradient(${gradientStops});`;
|
|
534
|
+
} else {
|
|
535
|
+
bgStyle += `background-color: ${colorToRgba(colors[0]!)};`;
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
const isEllipse = width > 0 && height > 0 && width !== height;
|
|
539
|
+
const pivotX = (pivot[0] ?? 0.5) * 100;
|
|
540
|
+
const pivotY = (pivot[1] ?? 0.5) * 100;
|
|
541
|
+
let sizePart = '';
|
|
542
|
+
if (width > 0 && height > 0) {
|
|
543
|
+
if (!isEllipse && width === height) {
|
|
544
|
+
sizePart = `${Math.round(width)}px`;
|
|
545
|
+
} else {
|
|
546
|
+
sizePart = `${Math.round(width)}px ${Math.round(height)}px`;
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
sizePart = 'closest-side';
|
|
550
|
+
}
|
|
551
|
+
const radialGradient = `radial-gradient(${isEllipse ? 'ellipse' : 'circle'} ${sizePart} at ${pivotX.toFixed(2)}% ${pivotY.toFixed(2)}%, ${gradientStops})`;
|
|
552
|
+
if (srcImg || gradient) {
|
|
553
|
+
maskStyle += `mask-image: ${radialGradient};`;
|
|
554
|
+
} else {
|
|
555
|
+
bgStyle += `background-image: ${radialGradient};`;
|
|
556
|
+
bgStyle += `background-repeat: no-repeat;`;
|
|
557
|
+
bgStyle += `background-size: 100% 100%;`;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if ('linear' in shaderProps) {
|
|
565
|
+
const lg = shaderProps.linear as
|
|
566
|
+
| Partial<lng.LinearGradientProps>
|
|
567
|
+
| undefined;
|
|
568
|
+
const colors = Array.isArray(lg?.colors) ? lg!.colors! : [];
|
|
569
|
+
const stops = Array.isArray(lg?.stops) ? lg!.stops! : undefined;
|
|
570
|
+
const angleRad = typeof lg?.angle === 'number' ? lg!.angle! : 0; // radians
|
|
571
|
+
|
|
572
|
+
if (colors.length > 0) {
|
|
573
|
+
const gradientStops = buildGradientStops(colors, stops);
|
|
574
|
+
if (gradientStops) {
|
|
575
|
+
if (colors.length === 1) {
|
|
576
|
+
if (srcImg || gradient) {
|
|
577
|
+
maskStyle += `mask-image: linear-gradient(${gradientStops});`;
|
|
578
|
+
} else {
|
|
579
|
+
bgStyle += `background-color: ${colorToRgba(colors[0]!)};`;
|
|
580
|
+
}
|
|
581
|
+
} else {
|
|
582
|
+
const angleDeg = 180 * (angleRad / Math.PI - 1);
|
|
583
|
+
const linearGradient = `linear-gradient(${angleDeg.toFixed(2)}deg, ${gradientStops})`;
|
|
584
|
+
if (srcImg || gradient) {
|
|
585
|
+
maskStyle += `mask-image: ${linearGradient};`;
|
|
586
|
+
} else {
|
|
587
|
+
bgStyle += `background-image: ${linearGradient};`;
|
|
588
|
+
bgStyle += `background-repeat: no-repeat;`;
|
|
589
|
+
bgStyle += `background-size: 100% 100%;`;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (maskStyle !== '') {
|
|
598
|
+
if (!supportsStandardMask && supportsWebkitMask) {
|
|
599
|
+
maskStyle = maskStyle.replace(/mask-/g, '-webkit-mask-');
|
|
600
|
+
} else if (!supportsCssMask) {
|
|
601
|
+
maskStyle = '';
|
|
602
|
+
}
|
|
603
|
+
if (maskStyle !== '') {
|
|
604
|
+
needsBackgroundLayer = true;
|
|
605
|
+
}
|
|
477
606
|
}
|
|
478
607
|
|
|
479
608
|
style += radiusStyle;
|
|
480
|
-
bgStyle += radiusStyle;
|
|
481
|
-
borderStyle += radiusStyle;
|
|
482
609
|
|
|
483
|
-
if (
|
|
484
|
-
|
|
610
|
+
if (needsBackgroundLayer) {
|
|
611
|
+
if (node.divBg == null) {
|
|
612
|
+
node.divBg = document.createElement('div');
|
|
613
|
+
node.div.insertBefore(node.divBg, node.div.firstChild);
|
|
614
|
+
} else if (node.divBg.parentElement !== node.div) {
|
|
615
|
+
node.div.insertBefore(node.divBg, node.div.firstChild);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
let bgLayerStyle =
|
|
619
|
+
'position: absolute; top:0; left:0; right:0; bottom:0; z-index: -1; pointer-events: none; overflow: hidden;';
|
|
620
|
+
if (bgStyle) {
|
|
621
|
+
bgLayerStyle += bgStyle;
|
|
622
|
+
}
|
|
623
|
+
if (maskStyle) {
|
|
624
|
+
bgLayerStyle += maskStyle;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
node.divBg.setAttribute('style', bgLayerStyle + radiusStyle);
|
|
628
|
+
|
|
629
|
+
if (rawImgSrc) {
|
|
630
|
+
if (!node.imgEl) {
|
|
631
|
+
node.imgEl = document.createElement('img');
|
|
632
|
+
node.imgEl.alt = '';
|
|
633
|
+
node.imgEl.crossOrigin = 'anonymous';
|
|
634
|
+
node.imgEl.setAttribute('aria-hidden', 'true');
|
|
635
|
+
node.imgEl.setAttribute('loading', 'lazy');
|
|
636
|
+
node.imgEl.removeAttribute('src');
|
|
637
|
+
|
|
638
|
+
node.imgEl.addEventListener('load', () => {
|
|
639
|
+
const payload: lng.NodeTextureLoadedPayload = {
|
|
640
|
+
type: 'texture',
|
|
641
|
+
dimensions: {
|
|
642
|
+
w: node.imgEl!.naturalWidth,
|
|
643
|
+
h: node.imgEl!.naturalHeight,
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
node.imgEl!.style.display = '';
|
|
647
|
+
applySubTextureScaling(
|
|
648
|
+
node,
|
|
649
|
+
node.imgEl!,
|
|
650
|
+
node.lazyImageSubTextureProps,
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
const resizeMode = (node.props.textureOptions as any)?.resizeMode;
|
|
654
|
+
const clipX = resizeMode?.clipX ?? 0.5;
|
|
655
|
+
const clipY = resizeMode?.clipY ?? 0.5;
|
|
656
|
+
computeLegacyObjectFit(
|
|
657
|
+
node,
|
|
658
|
+
node.imgEl!,
|
|
659
|
+
resizeMode,
|
|
660
|
+
clipX,
|
|
661
|
+
clipY,
|
|
662
|
+
node.lazyImageSubTextureProps,
|
|
663
|
+
supportsObjectFit,
|
|
664
|
+
supportsObjectPosition,
|
|
665
|
+
);
|
|
666
|
+
node.emit('loaded', payload);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
node.imgEl.addEventListener('error', () => {
|
|
670
|
+
if (node.imgEl) {
|
|
671
|
+
node.imgEl.removeAttribute('src');
|
|
672
|
+
node.imgEl.style.display = 'none';
|
|
673
|
+
node.imgEl.removeAttribute('data-rawSrc');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const failedSrc =
|
|
677
|
+
node.imgEl?.dataset.pendingSrc || node.lazyImagePendingSrc || '';
|
|
678
|
+
|
|
679
|
+
const payload: lng.NodeTextureFailedPayload = {
|
|
680
|
+
type: 'texture',
|
|
681
|
+
error: new Error(`Failed to load image: ${failedSrc}`),
|
|
682
|
+
};
|
|
683
|
+
node.emit('failed', payload);
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
node.lazyImagePendingSrc = rawImgSrc;
|
|
688
|
+
node.lazyImageSubTextureProps = srcPos;
|
|
689
|
+
node.imgEl.dataset.pendingSrc = rawImgSrc;
|
|
690
|
+
|
|
691
|
+
if (node.imgEl.parentElement !== node.divBg) {
|
|
692
|
+
node.divBg.appendChild(node.imgEl);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
node.imgEl.setAttribute('style', imgStyle);
|
|
696
|
+
|
|
697
|
+
if (hasDivBgTint) {
|
|
698
|
+
node.imgEl.style.visibility = 'hidden';
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (isRenderStateInBounds(node.renderState)) {
|
|
702
|
+
node.applyPendingImageSrc();
|
|
703
|
+
} else if (!node.imgEl.dataset.rawSrc) {
|
|
704
|
+
node.imgEl.removeAttribute('src');
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (
|
|
708
|
+
srcPos &&
|
|
709
|
+
node.imgEl.complete &&
|
|
710
|
+
node.imgEl.dataset.rawSrc === rawImgSrc
|
|
711
|
+
) {
|
|
712
|
+
applySubTextureScaling(node, node.imgEl, srcPos);
|
|
713
|
+
}
|
|
714
|
+
if (
|
|
715
|
+
!srcPos &&
|
|
716
|
+
node.imgEl.complete &&
|
|
717
|
+
(!supportsObjectFit || !supportsObjectPosition) &&
|
|
718
|
+
node.imgEl.dataset.rawSrc === rawImgSrc
|
|
719
|
+
) {
|
|
720
|
+
const resizeMode = (node.props.textureOptions as any)?.resizeMode;
|
|
721
|
+
const clipX = resizeMode?.clipX ?? 0.5;
|
|
722
|
+
const clipY = resizeMode?.clipY ?? 0.5;
|
|
723
|
+
computeLegacyObjectFit(
|
|
724
|
+
node,
|
|
725
|
+
node.imgEl,
|
|
726
|
+
resizeMode,
|
|
727
|
+
clipX,
|
|
728
|
+
clipY,
|
|
729
|
+
srcPos,
|
|
730
|
+
supportsObjectFit,
|
|
731
|
+
supportsObjectPosition,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
node.lazyImagePendingSrc = null;
|
|
736
|
+
node.lazyImageSubTextureProps = null;
|
|
737
|
+
if (node.imgEl) {
|
|
738
|
+
node.imgEl.remove();
|
|
739
|
+
node.imgEl = undefined;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
485
742
|
} else {
|
|
486
|
-
|
|
487
|
-
node.
|
|
743
|
+
node.lazyImagePendingSrc = null;
|
|
744
|
+
node.lazyImageSubTextureProps = null;
|
|
745
|
+
if (node.imgEl) {
|
|
746
|
+
node.imgEl.remove();
|
|
747
|
+
node.imgEl = undefined;
|
|
748
|
+
}
|
|
749
|
+
if (node.divBg) {
|
|
750
|
+
node.divBg.remove();
|
|
751
|
+
node.divBg = undefined;
|
|
752
|
+
}
|
|
753
|
+
style += bgStyle;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const needsSeparateBorderLayer = needsBackgroundLayer && maskStyle !== '';
|
|
757
|
+
|
|
758
|
+
if (needsSeparateBorderLayer) {
|
|
759
|
+
if (node.divBorder == null) {
|
|
760
|
+
node.divBorder = document.createElement('div');
|
|
761
|
+
node.div.appendChild(node.divBorder);
|
|
762
|
+
}
|
|
763
|
+
} else if (node.divBorder) {
|
|
764
|
+
node.divBorder.remove();
|
|
765
|
+
node.divBorder = undefined;
|
|
488
766
|
}
|
|
767
|
+
|
|
489
768
|
if (node.divBorder == null) {
|
|
490
769
|
style += borderStyle;
|
|
491
770
|
} else {
|
|
492
|
-
|
|
493
|
-
|
|
771
|
+
let borderLayerStyle =
|
|
772
|
+
'position: absolute; top:0; left:0; right:0; bottom:0; z-index: -1; pointer-events: none;';
|
|
773
|
+
borderLayerStyle += borderStyle;
|
|
774
|
+
node.divBorder.setAttribute('style', borderLayerStyle + radiusStyle);
|
|
494
775
|
}
|
|
495
776
|
}
|
|
496
777
|
|
|
497
|
-
node.div.setAttribute('style', style);
|
|
498
|
-
}
|
|
778
|
+
node.div.setAttribute('style', compactString(style));
|
|
499
779
|
|
|
500
|
-
|
|
780
|
+
if (node instanceof DOMNode && node !== node.stage.root) {
|
|
781
|
+
const hasTextureSrc = nodeHasTextureSource(node);
|
|
782
|
+
if (hasTextureSrc && node.boundsDirty) {
|
|
783
|
+
const next = computeRenderStateForNode(node);
|
|
784
|
+
if (next != null) {
|
|
785
|
+
node.updateRenderState(next);
|
|
786
|
+
}
|
|
787
|
+
node.boundsDirty = false;
|
|
788
|
+
} else if (!hasTextureSrc) {
|
|
789
|
+
node.boundsDirty = false;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
501
793
|
|
|
502
794
|
const textNodesToMeasure = new Set<DOMText>();
|
|
503
795
|
|
|
504
796
|
type Size = { width: number; height: number };
|
|
505
797
|
|
|
506
798
|
function getElSize(node: DOMNode): Size {
|
|
507
|
-
|
|
799
|
+
const rawRect = node.div.getBoundingClientRect();
|
|
508
800
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
801
|
+
const dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1;
|
|
802
|
+
let width = rawRect.width / dpr;
|
|
803
|
+
let height = rawRect.height / dpr;
|
|
512
804
|
|
|
513
805
|
for (;;) {
|
|
514
806
|
if (node.props.scale != null && node.props.scale !== 1) {
|
|
515
|
-
|
|
516
|
-
|
|
807
|
+
width /= node.props.scale;
|
|
808
|
+
height /= node.props.scale;
|
|
517
809
|
} else {
|
|
518
|
-
|
|
519
|
-
|
|
810
|
+
width /= node.props.scaleX;
|
|
811
|
+
height /= node.props.scaleY;
|
|
520
812
|
}
|
|
521
813
|
|
|
522
814
|
if (node.parent instanceof DOMNode) {
|
|
@@ -526,7 +818,7 @@ function getElSize(node: DOMNode): Size {
|
|
|
526
818
|
}
|
|
527
819
|
}
|
|
528
820
|
|
|
529
|
-
return
|
|
821
|
+
return { width, height };
|
|
530
822
|
}
|
|
531
823
|
|
|
532
824
|
/*
|
|
@@ -542,7 +834,6 @@ function updateDOMTextSize(node: DOMText): void {
|
|
|
542
834
|
if (node.props.h !== size.height) {
|
|
543
835
|
node.props.h = size.height;
|
|
544
836
|
updateNodeStyles(node);
|
|
545
|
-
node.emit('loaded');
|
|
546
837
|
}
|
|
547
838
|
break;
|
|
548
839
|
case 'none':
|
|
@@ -551,10 +842,21 @@ function updateDOMTextSize(node: DOMText): void {
|
|
|
551
842
|
node.props.w = size.width;
|
|
552
843
|
node.props.h = size.height;
|
|
553
844
|
updateNodeStyles(node);
|
|
554
|
-
node.emit('loaded');
|
|
555
845
|
}
|
|
556
846
|
break;
|
|
557
847
|
}
|
|
848
|
+
|
|
849
|
+
if (!node.loaded) {
|
|
850
|
+
const payload: lng.NodeTextLoadedPayload = {
|
|
851
|
+
type: 'text',
|
|
852
|
+
dimensions: {
|
|
853
|
+
w: node.props.w,
|
|
854
|
+
h: node.props.h,
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
node.emit('loaded', payload);
|
|
858
|
+
node.loaded = true;
|
|
859
|
+
}
|
|
558
860
|
}
|
|
559
861
|
|
|
560
862
|
function updateDOMTextMeasurements() {
|
|
@@ -566,16 +868,17 @@ function scheduleUpdateDOMTextMeasurement(node: DOMText) {
|
|
|
566
868
|
/*
|
|
567
869
|
Make sure the font is loaded before measuring
|
|
568
870
|
*/
|
|
569
|
-
if (node.fontFamily && !fontFamiliesToLoad.has(node.fontFamily)) {
|
|
570
|
-
fontFamiliesToLoad.add(node.fontFamily);
|
|
571
|
-
document.fonts.load(`16px ${node.fontFamily}`);
|
|
572
|
-
}
|
|
573
871
|
|
|
574
872
|
if (textNodesToMeasure.size === 0) {
|
|
873
|
+
const fonts = document.fonts;
|
|
575
874
|
if (document.fonts.status === 'loaded') {
|
|
576
875
|
setTimeout(updateDOMTextMeasurements);
|
|
577
876
|
} else {
|
|
578
|
-
|
|
877
|
+
if (fonts && fonts.ready && typeof fonts.ready.then === 'function') {
|
|
878
|
+
fonts.ready.then(updateDOMTextMeasurements);
|
|
879
|
+
} else {
|
|
880
|
+
setTimeout(updateDOMTextMeasurements, 500);
|
|
881
|
+
}
|
|
579
882
|
}
|
|
580
883
|
}
|
|
581
884
|
|
|
@@ -583,12 +886,13 @@ function scheduleUpdateDOMTextMeasurement(node: DOMText) {
|
|
|
583
886
|
}
|
|
584
887
|
|
|
585
888
|
function updateNodeData(node: DOMNode | DOMText) {
|
|
586
|
-
|
|
587
|
-
|
|
889
|
+
const data = node.data;
|
|
890
|
+
for (let key in data) {
|
|
891
|
+
let keyValue: unknown = data[key];
|
|
588
892
|
if (keyValue === undefined) {
|
|
589
893
|
node.div.removeAttribute('data-' + key);
|
|
590
894
|
} else {
|
|
591
|
-
node.div.
|
|
895
|
+
node.div.dataset[key] = String(keyValue);
|
|
592
896
|
}
|
|
593
897
|
}
|
|
594
898
|
}
|
|
@@ -596,7 +900,7 @@ function updateNodeData(node: DOMNode | DOMText) {
|
|
|
596
900
|
function resolveNodeDefaults(
|
|
597
901
|
props: Partial<IRendererNodeProps>,
|
|
598
902
|
): IRendererNodeProps {
|
|
599
|
-
const color = props.color ??
|
|
903
|
+
const color = props.color ?? 0x00000000;
|
|
600
904
|
|
|
601
905
|
return {
|
|
602
906
|
x: props.x ?? 0,
|
|
@@ -677,15 +981,31 @@ const defaultShader: IRendererShader = {
|
|
|
677
981
|
|
|
678
982
|
let lastNodeId = 0;
|
|
679
983
|
|
|
680
|
-
|
|
984
|
+
const CoreNodeRenderStateMap = new Map<number, string>([
|
|
985
|
+
[0, 'init'],
|
|
986
|
+
[2, 'outOfBounds'],
|
|
987
|
+
[4, 'inBounds'],
|
|
988
|
+
[8, 'inViewport'],
|
|
989
|
+
]);
|
|
990
|
+
|
|
991
|
+
export class DOMNode extends EventEmitter implements IRendererNode {
|
|
681
992
|
div = document.createElement('div');
|
|
682
993
|
divBg: HTMLElement | undefined;
|
|
683
994
|
divBorder: HTMLElement | undefined;
|
|
995
|
+
imgEl: HTMLImageElement | undefined;
|
|
996
|
+
lazyImagePendingSrc: string | null = null;
|
|
997
|
+
lazyImageSubTextureProps:
|
|
998
|
+
| InstanceType<lng.TextureMap['SubTexture']>['props']
|
|
999
|
+
| null = null;
|
|
1000
|
+
boundsDirty = true;
|
|
1001
|
+
children = new Set<DOMNode>();
|
|
684
1002
|
|
|
685
1003
|
id = ++lastNodeId;
|
|
686
1004
|
|
|
687
1005
|
renderState: lng.CoreNodeRenderState = 0 /* Init */;
|
|
688
1006
|
|
|
1007
|
+
preventCleanup = true;
|
|
1008
|
+
|
|
689
1009
|
constructor(
|
|
690
1010
|
public stage: IRendererStage,
|
|
691
1011
|
public props: IRendererNodeProps,
|
|
@@ -697,6 +1017,11 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
697
1017
|
this.div.setAttribute('data-id', String(this.id));
|
|
698
1018
|
elMap.set(this, this.div);
|
|
699
1019
|
|
|
1020
|
+
const parent = this.props.parent;
|
|
1021
|
+
if (parent instanceof DOMNode) {
|
|
1022
|
+
parent.children.add(this);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
700
1025
|
updateNodeParent(this);
|
|
701
1026
|
updateNodeStyles(this);
|
|
702
1027
|
updateNodeData(this);
|
|
@@ -704,6 +1029,10 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
704
1029
|
|
|
705
1030
|
destroy(): void {
|
|
706
1031
|
elMap.delete(this);
|
|
1032
|
+
const parent = this.props.parent;
|
|
1033
|
+
if (parent instanceof DOMNode) {
|
|
1034
|
+
parent.children.delete(this);
|
|
1035
|
+
}
|
|
707
1036
|
this.div.parentNode!.removeChild(this.div);
|
|
708
1037
|
}
|
|
709
1038
|
|
|
@@ -711,52 +1040,132 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
711
1040
|
return this.props.parent;
|
|
712
1041
|
}
|
|
713
1042
|
set parent(value: IRendererNode | null) {
|
|
1043
|
+
if (this.props.parent === value) return;
|
|
1044
|
+
|
|
1045
|
+
const prevParent = this.props.parent;
|
|
1046
|
+
if (prevParent instanceof DOMNode) {
|
|
1047
|
+
prevParent.children.delete(this);
|
|
1048
|
+
prevParent.markChildrenBoundsDirty();
|
|
1049
|
+
}
|
|
1050
|
+
|
|
714
1051
|
this.props.parent = value;
|
|
1052
|
+
|
|
1053
|
+
if (value instanceof DOMNode) {
|
|
1054
|
+
value.children.add(this);
|
|
1055
|
+
value.markChildrenBoundsDirty();
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
this.boundsDirty = true;
|
|
1059
|
+
this.markChildrenBoundsDirty();
|
|
715
1060
|
updateNodeParent(this);
|
|
716
1061
|
}
|
|
717
1062
|
|
|
1063
|
+
public markChildrenBoundsDirty() {
|
|
1064
|
+
for (const child of this.children) {
|
|
1065
|
+
child.boundsDirty = true;
|
|
1066
|
+
|
|
1067
|
+
if (child !== child.stage.root) {
|
|
1068
|
+
if (nodeHasTextureSource(child)) {
|
|
1069
|
+
const nextState = computeRenderStateForNode(child);
|
|
1070
|
+
if (nextState != null) {
|
|
1071
|
+
child.updateRenderState(nextState);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
child.boundsDirty = false;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
child.markChildrenBoundsDirty();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
718
1081
|
animate = animate;
|
|
719
1082
|
|
|
1083
|
+
updateRenderState(renderState: lng.CoreNodeRenderState) {
|
|
1084
|
+
if (renderState === this.renderState) return;
|
|
1085
|
+
const previous = this.renderState;
|
|
1086
|
+
this.renderState = renderState;
|
|
1087
|
+
const event = CoreNodeRenderStateMap.get(renderState);
|
|
1088
|
+
if (isRenderStateInBounds(renderState)) {
|
|
1089
|
+
this.applyPendingImageSrc();
|
|
1090
|
+
}
|
|
1091
|
+
if (event && event !== 'init') {
|
|
1092
|
+
this.emit(event, { previous, current: renderState });
|
|
1093
|
+
}
|
|
1094
|
+
if (this.imgEl) {
|
|
1095
|
+
this.imgEl.dataset.state = event;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
applyPendingImageSrc() {
|
|
1100
|
+
if (!this.imgEl) return;
|
|
1101
|
+
const pendingSrc = this.lazyImagePendingSrc;
|
|
1102
|
+
if (!pendingSrc) return;
|
|
1103
|
+
if (this.imgEl.dataset.rawSrc === pendingSrc) return;
|
|
1104
|
+
this.imgEl.style.display = '';
|
|
1105
|
+
this.imgEl.dataset.pendingSrc = pendingSrc;
|
|
1106
|
+
this.imgEl.src = pendingSrc;
|
|
1107
|
+
this.imgEl.dataset.rawSrc = pendingSrc;
|
|
1108
|
+
this.imgEl.dataset.pendingSrc = '';
|
|
1109
|
+
}
|
|
1110
|
+
|
|
720
1111
|
get x() {
|
|
721
1112
|
return this.props.x;
|
|
722
1113
|
}
|
|
723
1114
|
set x(v) {
|
|
1115
|
+
if (this.props.x === v) return;
|
|
724
1116
|
this.props.x = v;
|
|
1117
|
+
this.boundsDirty = true;
|
|
1118
|
+
this.markChildrenBoundsDirty();
|
|
725
1119
|
updateNodeStyles(this);
|
|
726
1120
|
}
|
|
727
1121
|
get y() {
|
|
728
1122
|
return this.props.y;
|
|
729
1123
|
}
|
|
730
1124
|
set y(v) {
|
|
1125
|
+
if (this.props.y === v) return;
|
|
731
1126
|
this.props.y = v;
|
|
1127
|
+
this.boundsDirty = true;
|
|
1128
|
+
this.markChildrenBoundsDirty();
|
|
732
1129
|
updateNodeStyles(this);
|
|
733
1130
|
}
|
|
734
1131
|
get w() {
|
|
735
1132
|
return this.props.w;
|
|
736
1133
|
}
|
|
737
1134
|
set w(v) {
|
|
1135
|
+
if (this.props.w === v) return;
|
|
738
1136
|
this.props.w = v;
|
|
1137
|
+
this.boundsDirty = true;
|
|
1138
|
+
this.markChildrenBoundsDirty();
|
|
739
1139
|
updateNodeStyles(this);
|
|
740
1140
|
}
|
|
741
1141
|
get h() {
|
|
742
1142
|
return this.props.h;
|
|
743
1143
|
}
|
|
744
1144
|
set h(v) {
|
|
1145
|
+
if (this.props.h === v) return;
|
|
745
1146
|
this.props.h = v;
|
|
1147
|
+
this.boundsDirty = true;
|
|
1148
|
+
this.markChildrenBoundsDirty();
|
|
746
1149
|
updateNodeStyles(this);
|
|
747
1150
|
}
|
|
748
1151
|
get width() {
|
|
749
1152
|
return this.props.w;
|
|
750
1153
|
}
|
|
751
1154
|
set width(v) {
|
|
1155
|
+
if (this.props.w === v) return;
|
|
752
1156
|
this.props.w = v;
|
|
1157
|
+
this.boundsDirty = true;
|
|
1158
|
+
this.markChildrenBoundsDirty();
|
|
753
1159
|
updateNodeStyles(this);
|
|
754
1160
|
}
|
|
755
1161
|
get height() {
|
|
756
1162
|
return this.props.h;
|
|
757
1163
|
}
|
|
758
1164
|
set height(v) {
|
|
1165
|
+
if (this.props.h === v) return;
|
|
759
1166
|
this.props.h = v;
|
|
1167
|
+
this.boundsDirty = true;
|
|
1168
|
+
this.markChildrenBoundsDirty();
|
|
760
1169
|
updateNodeStyles(this);
|
|
761
1170
|
}
|
|
762
1171
|
get alpha() {
|
|
@@ -847,14 +1256,17 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
847
1256
|
return this.props.zIndex;
|
|
848
1257
|
}
|
|
849
1258
|
set zIndex(v) {
|
|
850
|
-
this.props.zIndex
|
|
1259
|
+
if (this.props.zIndex === v) return;
|
|
1260
|
+
this.props.zIndex = Math.ceil(v);
|
|
851
1261
|
updateNodeStyles(this);
|
|
852
1262
|
}
|
|
853
1263
|
get texture() {
|
|
854
1264
|
return this.props.texture;
|
|
855
1265
|
}
|
|
856
1266
|
set texture(v) {
|
|
1267
|
+
if (this.props.texture === v) return;
|
|
857
1268
|
this.props.texture = v;
|
|
1269
|
+
this.boundsDirty = true;
|
|
858
1270
|
updateNodeStyles(this);
|
|
859
1271
|
}
|
|
860
1272
|
get textureOptions(): IRendererNode['textureOptions'] {
|
|
@@ -868,13 +1280,16 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
868
1280
|
return this.props.src;
|
|
869
1281
|
}
|
|
870
1282
|
set src(v) {
|
|
1283
|
+
if (this.props.src === v) return;
|
|
871
1284
|
this.props.src = v;
|
|
1285
|
+
this.boundsDirty = true;
|
|
872
1286
|
updateNodeStyles(this);
|
|
873
1287
|
}
|
|
874
1288
|
get scale() {
|
|
875
1289
|
return this.props.scale ?? 1;
|
|
876
1290
|
}
|
|
877
1291
|
set scale(v) {
|
|
1292
|
+
if (this.props.scale === v) return;
|
|
878
1293
|
this.props.scale = v;
|
|
879
1294
|
updateNodeStyles(this);
|
|
880
1295
|
}
|
|
@@ -955,6 +1370,7 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
955
1370
|
this.props.shader = v;
|
|
956
1371
|
updateNodeStyles(this);
|
|
957
1372
|
}
|
|
1373
|
+
|
|
958
1374
|
get data(): IRendererNode['data'] {
|
|
959
1375
|
return this.props.data;
|
|
960
1376
|
}
|
|
@@ -999,29 +1415,45 @@ class DOMNode extends EventEmitter implements IRendererNode {
|
|
|
999
1415
|
}
|
|
1000
1416
|
set boundsMargin(value: number | [number, number, number, number] | null) {
|
|
1001
1417
|
this.props.boundsMargin = value;
|
|
1418
|
+
this.boundsDirty = true;
|
|
1419
|
+
this.markChildrenBoundsDirty();
|
|
1002
1420
|
}
|
|
1003
1421
|
|
|
1004
1422
|
get absX(): number {
|
|
1005
|
-
|
|
1423
|
+
const parent = this.props.parent;
|
|
1424
|
+
return (
|
|
1425
|
+
this.x +
|
|
1426
|
+
-this.w * this.mountX +
|
|
1427
|
+
(parent instanceof DOMNode ? parent.absX : 0)
|
|
1428
|
+
);
|
|
1006
1429
|
}
|
|
1007
1430
|
get absY(): number {
|
|
1008
|
-
|
|
1431
|
+
const parent = this.props.parent;
|
|
1432
|
+
return (
|
|
1433
|
+
this.y +
|
|
1434
|
+
-this.h * this.mountY +
|
|
1435
|
+
(parent instanceof DOMNode ? parent.absY : 0)
|
|
1436
|
+
);
|
|
1009
1437
|
}
|
|
1010
1438
|
}
|
|
1011
1439
|
|
|
1012
1440
|
class DOMText extends DOMNode {
|
|
1441
|
+
public loaded = false;
|
|
1442
|
+
|
|
1013
1443
|
constructor(
|
|
1014
1444
|
stage: IRendererStage,
|
|
1015
1445
|
public override props: IRendererTextNodeProps,
|
|
1016
1446
|
) {
|
|
1017
1447
|
super(stage, props);
|
|
1018
1448
|
this.div.innerText = props.text;
|
|
1449
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1019
1450
|
}
|
|
1020
1451
|
|
|
1021
1452
|
get text() {
|
|
1022
1453
|
return this.props.text;
|
|
1023
1454
|
}
|
|
1024
1455
|
set text(v) {
|
|
1456
|
+
if (this.props.text === v) return;
|
|
1025
1457
|
this.props.text = v;
|
|
1026
1458
|
this.div.innerText = v;
|
|
1027
1459
|
scheduleUpdateDOMTextMeasurement(this);
|
|
@@ -1030,29 +1462,46 @@ class DOMText extends DOMNode {
|
|
|
1030
1462
|
return this.props.fontFamily;
|
|
1031
1463
|
}
|
|
1032
1464
|
set fontFamily(v) {
|
|
1465
|
+
if (this.props.fontFamily === v) return;
|
|
1033
1466
|
this.props.fontFamily = v;
|
|
1034
1467
|
updateNodeStyles(this);
|
|
1468
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1035
1469
|
}
|
|
1036
1470
|
get fontSize() {
|
|
1037
1471
|
return this.props.fontSize;
|
|
1038
1472
|
}
|
|
1039
1473
|
set fontSize(v) {
|
|
1474
|
+
if (this.props.fontSize === v) return;
|
|
1040
1475
|
this.props.fontSize = v;
|
|
1041
1476
|
updateNodeStyles(this);
|
|
1477
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1042
1478
|
}
|
|
1043
1479
|
get fontStyle() {
|
|
1044
1480
|
return this.props.fontStyle;
|
|
1045
1481
|
}
|
|
1046
1482
|
set fontStyle(v) {
|
|
1483
|
+
if (this.props.fontStyle === v) return;
|
|
1047
1484
|
this.props.fontStyle = v;
|
|
1048
1485
|
updateNodeStyles(this);
|
|
1486
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1049
1487
|
}
|
|
1050
1488
|
get fontWeight() {
|
|
1051
1489
|
return this.props.fontWeight;
|
|
1052
1490
|
}
|
|
1053
1491
|
set fontWeight(v) {
|
|
1492
|
+
if (this.props.fontWeight === v) return;
|
|
1054
1493
|
this.props.fontWeight = v;
|
|
1055
1494
|
updateNodeStyles(this);
|
|
1495
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1496
|
+
}
|
|
1497
|
+
get fontStretch() {
|
|
1498
|
+
return this.props.fontStretch;
|
|
1499
|
+
}
|
|
1500
|
+
set fontStretch(v) {
|
|
1501
|
+
if (this.props.fontStretch === v) return;
|
|
1502
|
+
this.props.fontStretch = v;
|
|
1503
|
+
updateNodeStyles(this);
|
|
1504
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1056
1505
|
}
|
|
1057
1506
|
get forceLoad() {
|
|
1058
1507
|
return this.props.forceLoad;
|
|
@@ -1064,34 +1513,43 @@ class DOMText extends DOMNode {
|
|
|
1064
1513
|
return this.props.lineHeight;
|
|
1065
1514
|
}
|
|
1066
1515
|
set lineHeight(v) {
|
|
1516
|
+
if (this.props.lineHeight === v) return;
|
|
1067
1517
|
this.props.lineHeight = v;
|
|
1068
1518
|
updateNodeStyles(this);
|
|
1519
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1069
1520
|
}
|
|
1070
1521
|
get maxWidth() {
|
|
1071
1522
|
return this.props.maxWidth;
|
|
1072
1523
|
}
|
|
1073
1524
|
set maxWidth(v) {
|
|
1525
|
+
if (this.props.maxWidth === v) return;
|
|
1074
1526
|
this.props.maxWidth = v;
|
|
1075
1527
|
updateNodeStyles(this);
|
|
1528
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1076
1529
|
}
|
|
1077
1530
|
get maxHeight() {
|
|
1078
1531
|
return this.props.maxHeight;
|
|
1079
1532
|
}
|
|
1080
1533
|
set maxHeight(v) {
|
|
1534
|
+
if (this.props.maxHeight === v) return;
|
|
1081
1535
|
this.props.maxHeight = v;
|
|
1082
1536
|
updateNodeStyles(this);
|
|
1537
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1083
1538
|
}
|
|
1084
1539
|
get letterSpacing() {
|
|
1085
1540
|
return this.props.letterSpacing;
|
|
1086
1541
|
}
|
|
1087
1542
|
set letterSpacing(v) {
|
|
1543
|
+
if (this.props.letterSpacing === v) return;
|
|
1088
1544
|
this.props.letterSpacing = v;
|
|
1089
1545
|
updateNodeStyles(this);
|
|
1546
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1090
1547
|
}
|
|
1091
1548
|
get textAlign() {
|
|
1092
1549
|
return this.props.textAlign;
|
|
1093
1550
|
}
|
|
1094
1551
|
set textAlign(v) {
|
|
1552
|
+
if (this.props.textAlign === v) return;
|
|
1095
1553
|
this.props.textAlign = v;
|
|
1096
1554
|
updateNodeStyles(this);
|
|
1097
1555
|
}
|
|
@@ -1099,6 +1557,7 @@ class DOMText extends DOMNode {
|
|
|
1099
1557
|
return this.props.overflowSuffix;
|
|
1100
1558
|
}
|
|
1101
1559
|
set overflowSuffix(v) {
|
|
1560
|
+
if (this.props.overflowSuffix === v) return;
|
|
1102
1561
|
this.props.overflowSuffix = v;
|
|
1103
1562
|
updateNodeStyles(this);
|
|
1104
1563
|
}
|
|
@@ -1106,15 +1565,19 @@ class DOMText extends DOMNode {
|
|
|
1106
1565
|
return this.props.maxLines;
|
|
1107
1566
|
}
|
|
1108
1567
|
set maxLines(v) {
|
|
1568
|
+
if (this.props.maxLines === v) return;
|
|
1109
1569
|
this.props.maxLines = v;
|
|
1110
1570
|
updateNodeStyles(this);
|
|
1571
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1111
1572
|
}
|
|
1112
1573
|
get contain() {
|
|
1113
1574
|
return this.props.contain;
|
|
1114
1575
|
}
|
|
1115
1576
|
set contain(v) {
|
|
1577
|
+
if (this.props.contain === v) return;
|
|
1116
1578
|
this.props.contain = v;
|
|
1117
1579
|
updateNodeStyles(this);
|
|
1580
|
+
scheduleUpdateDOMTextMeasurement(this);
|
|
1118
1581
|
}
|
|
1119
1582
|
get verticalAlign() {
|
|
1120
1583
|
return this.props.verticalAlign;
|
|
@@ -1171,11 +1634,12 @@ function updateRootPosition(this: DOMRendererMain) {
|
|
|
1171
1634
|
export class DOMRendererMain implements IRendererMain {
|
|
1172
1635
|
root: DOMNode;
|
|
1173
1636
|
canvas: HTMLCanvasElement;
|
|
1174
|
-
|
|
1175
1637
|
stage: IRendererStage;
|
|
1638
|
+
private eventListeners: Map<string, Set<(target: any, data: any) => void>> =
|
|
1639
|
+
new Map();
|
|
1176
1640
|
|
|
1177
1641
|
constructor(
|
|
1178
|
-
public settings:
|
|
1642
|
+
public settings: DomRendererMainSettings,
|
|
1179
1643
|
rawTarget: string | HTMLElement,
|
|
1180
1644
|
) {
|
|
1181
1645
|
let target: HTMLElement;
|
|
@@ -1203,15 +1667,20 @@ export class DOMRendererMain implements IRendererMain {
|
|
|
1203
1667
|
root: null!,
|
|
1204
1668
|
renderer: {
|
|
1205
1669
|
mode: 'canvas',
|
|
1670
|
+
boundsMargin: settings.boundsMargin,
|
|
1206
1671
|
},
|
|
1207
|
-
loadFont: async () => {},
|
|
1208
1672
|
shManager: {
|
|
1209
1673
|
registerShaderType() {},
|
|
1210
1674
|
},
|
|
1211
1675
|
animationManager: {
|
|
1212
|
-
registerAnimation() {
|
|
1213
|
-
|
|
1676
|
+
registerAnimation(anim) {
|
|
1677
|
+
console.log('registerAnimation', anim);
|
|
1678
|
+
},
|
|
1679
|
+
unregisterAnimation(anim) {
|
|
1680
|
+
console.log('unregisterAnimation', anim);
|
|
1681
|
+
},
|
|
1214
1682
|
},
|
|
1683
|
+
loadFont: async () => {},
|
|
1215
1684
|
cleanup() {},
|
|
1216
1685
|
};
|
|
1217
1686
|
|
|
@@ -1221,7 +1690,7 @@ export class DOMRendererMain implements IRendererMain {
|
|
|
1221
1690
|
w: settings.appWidth ?? 1920,
|
|
1222
1691
|
h: settings.appHeight ?? 1080,
|
|
1223
1692
|
shader: defaultShader,
|
|
1224
|
-
zIndex:
|
|
1693
|
+
zIndex: 1,
|
|
1225
1694
|
}),
|
|
1226
1695
|
);
|
|
1227
1696
|
this.stage.root = this.root;
|
|
@@ -1255,6 +1724,77 @@ export class DOMRendererMain implements IRendererMain {
|
|
|
1255
1724
|
window.addEventListener('resize', updateRootPosition.bind(this));
|
|
1256
1725
|
}
|
|
1257
1726
|
|
|
1727
|
+
removeAllListeners(): void {
|
|
1728
|
+
if (this.eventListeners.size === 0) return;
|
|
1729
|
+
this.eventListeners.forEach((listeners) => listeners.clear());
|
|
1730
|
+
this.eventListeners.clear();
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
once<K extends string | number>(
|
|
1734
|
+
event: Extract<K, string>,
|
|
1735
|
+
listener: { [s: string]: (target: any, data: any) => void }[K],
|
|
1736
|
+
): void {
|
|
1737
|
+
const wrappedListener = (target: any, data: any) => {
|
|
1738
|
+
this.off(event, wrappedListener);
|
|
1739
|
+
listener(target, data);
|
|
1740
|
+
};
|
|
1741
|
+
this.on(event, wrappedListener);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
on(name: string, callback: (target: any, data: any) => void) {
|
|
1745
|
+
let listeners = this.eventListeners.get(name);
|
|
1746
|
+
if (!listeners) {
|
|
1747
|
+
listeners = new Set();
|
|
1748
|
+
this.eventListeners.set(name, listeners);
|
|
1749
|
+
}
|
|
1750
|
+
listeners.add(callback);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
off<K extends string | number>(
|
|
1754
|
+
event: Extract<K, string>,
|
|
1755
|
+
listener: { [s: string]: (target: any, data: any) => void }[K],
|
|
1756
|
+
): void {
|
|
1757
|
+
const listeners = this.eventListeners.get(event);
|
|
1758
|
+
if (listeners) {
|
|
1759
|
+
listeners.delete(listener);
|
|
1760
|
+
if (listeners.size === 0) {
|
|
1761
|
+
this.eventListeners.delete(event);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
emit<K extends string | number>(
|
|
1767
|
+
event: Extract<K, string>,
|
|
1768
|
+
data: Parameters<any>[1],
|
|
1769
|
+
): void;
|
|
1770
|
+
emit<K extends string | number>(
|
|
1771
|
+
event: Extract<K, string>,
|
|
1772
|
+
target: any,
|
|
1773
|
+
data: Parameters<any>[1],
|
|
1774
|
+
): void;
|
|
1775
|
+
emit<K extends string | number>(
|
|
1776
|
+
event: Extract<K, string>,
|
|
1777
|
+
targetOrData: any,
|
|
1778
|
+
maybeData?: Parameters<any>[1],
|
|
1779
|
+
): void {
|
|
1780
|
+
const listeners = this.eventListeners.get(event);
|
|
1781
|
+
if (!listeners || listeners.size === 0) {
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
const hasExplicitTarget = arguments.length === 3;
|
|
1786
|
+
const target = hasExplicitTarget ? targetOrData : this.root;
|
|
1787
|
+
const data = hasExplicitTarget ? maybeData : targetOrData;
|
|
1788
|
+
|
|
1789
|
+
for (const listener of Array.from(listeners)) {
|
|
1790
|
+
try {
|
|
1791
|
+
listener(target, data);
|
|
1792
|
+
} catch (error) {
|
|
1793
|
+
console.error(`Error in listener for event "${event}"`, error);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1258
1798
|
createNode(props: Partial<IRendererNodeProps>): IRendererNode {
|
|
1259
1799
|
return new DOMNode(this.stage, resolveNodeDefaults(props));
|
|
1260
1800
|
}
|
|
@@ -1263,17 +1803,32 @@ export class DOMRendererMain implements IRendererMain {
|
|
|
1263
1803
|
return new DOMText(this.stage, resolveTextNodeDefaults(props));
|
|
1264
1804
|
}
|
|
1265
1805
|
|
|
1806
|
+
/** TODO: restore this */
|
|
1807
|
+
// createShader<ShType extends keyof ShaderMap>(
|
|
1808
|
+
// shType: ShType,
|
|
1809
|
+
// props?: OptionalShaderProps<ShType>,
|
|
1810
|
+
// ): InstanceType<lng.ShaderMap[ShType]> {
|
|
1811
|
+
// return { shaderType: shType, props, program: {} } as InstanceType<
|
|
1812
|
+
// lng.ShaderMap[ShType]
|
|
1813
|
+
// >;
|
|
1814
|
+
// }
|
|
1815
|
+
|
|
1266
1816
|
createShader(
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
return {
|
|
1817
|
+
...args: Parameters<typeof lng.RendererMain.prototype.createShader>
|
|
1818
|
+
): ReturnType<typeof lng.RendererMain.prototype.createShader> {
|
|
1819
|
+
const [shaderType, props] = args;
|
|
1820
|
+
return {
|
|
1821
|
+
// @ts-ignore
|
|
1822
|
+
shaderType,
|
|
1823
|
+
props,
|
|
1824
|
+
program: {},
|
|
1825
|
+
};
|
|
1271
1826
|
}
|
|
1272
1827
|
|
|
1273
|
-
createTexture(
|
|
1274
|
-
textureType:
|
|
1275
|
-
props:
|
|
1276
|
-
):
|
|
1828
|
+
createTexture<Type extends keyof lng.TextureMap>(
|
|
1829
|
+
textureType: Type,
|
|
1830
|
+
props: ExtractProps<lng.TextureMap[Type]>,
|
|
1831
|
+
): InstanceType<lng.TextureMap[Type]> {
|
|
1277
1832
|
let type = lng.TextureType.generic;
|
|
1278
1833
|
switch (textureType) {
|
|
1279
1834
|
case 'SubTexture':
|
|
@@ -1292,10 +1847,29 @@ export class DOMRendererMain implements IRendererMain {
|
|
|
1292
1847
|
type = lng.TextureType.renderToTexture;
|
|
1293
1848
|
break;
|
|
1294
1849
|
}
|
|
1295
|
-
return { type, props }
|
|
1850
|
+
return { type, props } as InstanceType<lng.TextureMap[Type]>;
|
|
1296
1851
|
}
|
|
1852
|
+
}
|
|
1297
1853
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1854
|
+
export function loadFontToDom(font: FontLoadOptions): void {
|
|
1855
|
+
// fontFamily: string;
|
|
1856
|
+
// metrics?: FontMetrics;
|
|
1857
|
+
// fontUrl?: string;
|
|
1858
|
+
// atlasUrl?: string;
|
|
1859
|
+
// atlasDataUrl?: string;
|
|
1860
|
+
|
|
1861
|
+
const fontFace = new FontFace(font.fontFamily, `url(${font.fontUrl})`);
|
|
1862
|
+
|
|
1863
|
+
if (typeof document !== 'undefined' && 'fonts' in document) {
|
|
1864
|
+
const fontSet = document.fonts as FontFaceSet & {
|
|
1865
|
+
add?: (font: FontFace) => FontFaceSet;
|
|
1866
|
+
};
|
|
1867
|
+
fontSet.add?.(fontFace);
|
|
1300
1868
|
}
|
|
1301
1869
|
}
|
|
1870
|
+
|
|
1871
|
+
export function isDomRenderer(
|
|
1872
|
+
r: lng.RendererMain | DOMRendererMain,
|
|
1873
|
+
): r is DOMRendererMain {
|
|
1874
|
+
return r instanceof DOMRendererMain;
|
|
1875
|
+
}
|