@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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import * as lng from '@lightningjs/renderer';
|
|
2
|
+
import { CoreAnimation } from '../intrinsicTypes.js';
|
|
3
|
+
import { EventEmitter } from '@lightningjs/renderer/utils';
|
|
4
|
+
import {
|
|
5
|
+
ShaderBorderPrefixedProps,
|
|
6
|
+
ShaderHolePunchProps,
|
|
7
|
+
ShaderLinearGradientProps,
|
|
8
|
+
ShaderRadialGradientProps,
|
|
9
|
+
ShaderRoundedProps,
|
|
10
|
+
ShaderShadowPrefixedProps,
|
|
11
|
+
} from '../shaders.js';
|
|
12
|
+
|
|
13
|
+
/** Based on {@link lng.CoreRenderer} */
|
|
14
|
+
export interface IRendererCoreRenderer {
|
|
15
|
+
mode: 'canvas' | 'webgl' | undefined;
|
|
16
|
+
boundsMargin?: number | [number, number, number, number];
|
|
17
|
+
}
|
|
18
|
+
/** Based on {@link lng.TrFontManager} */
|
|
19
|
+
export interface IRendererFontManager {
|
|
20
|
+
addFontFace: (...a: any[]) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Based on {@link lng.Stage} */
|
|
23
|
+
export interface IRendererStage {
|
|
24
|
+
root: IRendererNode;
|
|
25
|
+
renderer: IRendererCoreRenderer;
|
|
26
|
+
shManager: IRendererShaderManager;
|
|
27
|
+
animationManager: {
|
|
28
|
+
registerAnimation: (anim: CoreAnimation) => void;
|
|
29
|
+
unregisterAnimation: (anim: CoreAnimation) => void;
|
|
30
|
+
};
|
|
31
|
+
loadFont: lng.Stage['loadFont'];
|
|
32
|
+
reprocessUpdates?: (callback?: () => void) => void;
|
|
33
|
+
cleanup(full: boolean): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Based on {@link lng.CoreShaderManager} */
|
|
37
|
+
export interface IRendererShaderManager {
|
|
38
|
+
registerShaderType: (name: string, shader: any) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Based on {@link lng.CoreShaderType} */
|
|
42
|
+
export interface IRendererShaderType {}
|
|
43
|
+
|
|
44
|
+
export type IRendererShaderProps = Partial<ShaderBorderPrefixedProps> &
|
|
45
|
+
Partial<ShaderShadowPrefixedProps> &
|
|
46
|
+
Partial<ShaderRoundedProps> &
|
|
47
|
+
Partial<ShaderHolePunchProps> &
|
|
48
|
+
Partial<ShaderRadialGradientProps> &
|
|
49
|
+
Partial<ShaderLinearGradientProps>;
|
|
50
|
+
|
|
51
|
+
/** Based on {@link lng.CoreShaderNode} */
|
|
52
|
+
export interface IRendererShader extends Partial<lng.CoreShaderType> {
|
|
53
|
+
shaderType: IRendererShaderType;
|
|
54
|
+
props?: IRendererShaderProps;
|
|
55
|
+
program?: {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type ExtractProps<Type> = Type extends { z$__type__Props: infer Props }
|
|
59
|
+
? Props
|
|
60
|
+
: never;
|
|
61
|
+
|
|
62
|
+
export interface IEventEmitter<
|
|
63
|
+
T extends object = { [s: string]: (target: any, data: any) => void },
|
|
64
|
+
> {
|
|
65
|
+
on<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
|
|
66
|
+
once<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
|
|
67
|
+
off<K extends keyof T>(event: Extract<K, string>, listener: T[K]): void;
|
|
68
|
+
emit<K extends keyof T>(
|
|
69
|
+
event: Extract<K, string>,
|
|
70
|
+
data: Parameters<any>[1],
|
|
71
|
+
): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface IRendererNodeShaded extends EventEmitter {
|
|
75
|
+
stage: IRendererStage;
|
|
76
|
+
id: number;
|
|
77
|
+
animate: (
|
|
78
|
+
props: Partial<lng.INodeAnimateProps<any>>,
|
|
79
|
+
settings: Partial<lng.AnimationSettings>,
|
|
80
|
+
) => lng.IAnimationController;
|
|
81
|
+
get absX(): number;
|
|
82
|
+
get absY(): number;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Based on {@link lng.INodeProps} */
|
|
86
|
+
export interface IRendererNodeProps
|
|
87
|
+
extends Omit<lng.INodeProps, 'shader' | 'parent'> {
|
|
88
|
+
shader: IRendererShader | null;
|
|
89
|
+
parent: IRendererNode | null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Based on {@link lng.CoreNode} */
|
|
93
|
+
export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps {
|
|
94
|
+
div?: HTMLElement;
|
|
95
|
+
props: IRendererNodeProps;
|
|
96
|
+
renderState: lng.CoreNodeRenderState;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Based on {@link lng.ITextNodeProps} */
|
|
100
|
+
export interface IRendererTextNodeProps
|
|
101
|
+
extends Omit<lng.ITextNodeProps, 'shader' | 'parent'> {
|
|
102
|
+
shader: IRendererShader | null;
|
|
103
|
+
parent: IRendererNode | null;
|
|
104
|
+
fontWeight?: string;
|
|
105
|
+
fontStretch?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Based on {@link lng.ITextNode} */
|
|
109
|
+
export interface IRendererTextNode
|
|
110
|
+
extends IRendererNodeShaded,
|
|
111
|
+
IRendererTextNodeProps {
|
|
112
|
+
div?: HTMLElement;
|
|
113
|
+
props: IRendererTextNodeProps;
|
|
114
|
+
renderState: lng.CoreNodeRenderState;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Based on {@link lng.RendererMain} */
|
|
118
|
+
export interface IRendererMain extends IEventEmitter {
|
|
119
|
+
root: IRendererNode;
|
|
120
|
+
stage: IRendererStage;
|
|
121
|
+
canvas: HTMLCanvasElement;
|
|
122
|
+
createTextNode(props: Partial<IRendererTextNodeProps>): IRendererTextNode;
|
|
123
|
+
createNode(props: Partial<IRendererNodeProps>): IRendererNode;
|
|
124
|
+
createShader: typeof lng.RendererMain.prototype.createShader;
|
|
125
|
+
createTexture: typeof lng.RendererMain.prototype.createTexture;
|
|
126
|
+
//createEffect: typeof lng.RendererMain.prototype.createEffect;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface DomRendererMainSettings {
|
|
130
|
+
/**
|
|
131
|
+
* The logical width of the application (default: 1920)
|
|
132
|
+
*/
|
|
133
|
+
appWidth?: number;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The logical height of the application (default: 1080)
|
|
137
|
+
*/
|
|
138
|
+
appHeight?: number;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Device logical pixel ratio (default: 1)
|
|
142
|
+
*/
|
|
143
|
+
deviceLogicalPixelRatio?: number;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Bounds margin for the renderer
|
|
147
|
+
* Can be a single number (applied to all sides) or an array [top, right, bottom, left]
|
|
148
|
+
*/
|
|
149
|
+
boundsMargin?: number | [number, number, number, number];
|
|
150
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// Utilities extracted from domRenderer.ts for clarity
|
|
2
|
+
import * as lng from '@lightningjs/renderer';
|
|
3
|
+
import { Config } from '../config.js';
|
|
4
|
+
import { DOMNode } from './domRenderer.js';
|
|
5
|
+
import { isFunc } from '../utils.js';
|
|
6
|
+
|
|
7
|
+
// #region Color & Gradient Utils
|
|
8
|
+
|
|
9
|
+
export const colorToRgba = (c: number) =>
|
|
10
|
+
`rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`;
|
|
11
|
+
|
|
12
|
+
export function buildGradientStops(colors: number[], stops?: number[]): string {
|
|
13
|
+
if (!Array.isArray(colors) || colors.length === 0) return '';
|
|
14
|
+
const positions: number[] = [];
|
|
15
|
+
if (Array.isArray(stops) && stops.length === colors.length) {
|
|
16
|
+
for (let v of stops) {
|
|
17
|
+
if (typeof v !== 'number' || !isFinite(v)) {
|
|
18
|
+
positions.push(0);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
let pct = v <= 1 ? v * 100 : v;
|
|
22
|
+
if (pct < 0) pct = 0;
|
|
23
|
+
if (pct > 100) pct = 100;
|
|
24
|
+
positions.push(pct);
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
const lastIndex = colors.length - 1;
|
|
28
|
+
for (let i = 0; i < colors.length; i++) {
|
|
29
|
+
positions.push(lastIndex === 0 ? 0 : (i / lastIndex) * 100);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (positions.length !== colors.length) {
|
|
33
|
+
while (positions.length < colors.length)
|
|
34
|
+
positions.push(positions.length === 0 ? 0 : 100);
|
|
35
|
+
}
|
|
36
|
+
return colors
|
|
37
|
+
.map((color, idx) => `${colorToRgba(color)} ${positions[idx]!.toFixed(2)}%`)
|
|
38
|
+
.join(', ');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getNodeLineHeight(props: {
|
|
42
|
+
lineHeight?: number;
|
|
43
|
+
fontSize: number;
|
|
44
|
+
}): number {
|
|
45
|
+
return (
|
|
46
|
+
props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Legacy object-fit fall back for unsupported browsers */
|
|
51
|
+
export function computeLegacyObjectFit(
|
|
52
|
+
node: DOMNode,
|
|
53
|
+
img: HTMLImageElement,
|
|
54
|
+
resizeMode: ({ type?: string } & Record<string, any>) | undefined,
|
|
55
|
+
clipX: number,
|
|
56
|
+
clipY: number,
|
|
57
|
+
srcPos: null | { x: number; y: number },
|
|
58
|
+
supportsObjectFit: boolean,
|
|
59
|
+
supportsObjectPosition: boolean,
|
|
60
|
+
) {
|
|
61
|
+
if (supportsObjectFit && supportsObjectPosition) return;
|
|
62
|
+
const containerW = node.props.w || img.naturalWidth;
|
|
63
|
+
const containerH = node.props.h || img.naturalHeight;
|
|
64
|
+
const naturalW = img.naturalWidth || 1;
|
|
65
|
+
const naturalH = img.naturalHeight || 1;
|
|
66
|
+
let fitType = resizeMode?.type || (srcPos ? 'none' : 'fill');
|
|
67
|
+
let drawW = naturalW;
|
|
68
|
+
let drawH = naturalH;
|
|
69
|
+
switch (fitType) {
|
|
70
|
+
case 'cover': {
|
|
71
|
+
const scale = Math.max(containerW / naturalW, containerH / naturalH);
|
|
72
|
+
drawW = naturalW * scale;
|
|
73
|
+
drawH = naturalH * scale;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'contain': {
|
|
77
|
+
const scale = Math.min(containerW / naturalW, containerH / naturalH);
|
|
78
|
+
drawW = naturalW * scale;
|
|
79
|
+
drawH = naturalH * scale;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'fill': {
|
|
83
|
+
drawW = containerW;
|
|
84
|
+
drawH = containerH;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let offsetX = (containerW - drawW) * clipX;
|
|
89
|
+
let offsetY = (containerH - drawH) * clipY;
|
|
90
|
+
if (srcPos) {
|
|
91
|
+
offsetX = -srcPos.x;
|
|
92
|
+
offsetY = -srcPos.y;
|
|
93
|
+
}
|
|
94
|
+
const styleParts = [
|
|
95
|
+
'position: absolute',
|
|
96
|
+
`width: ${Math.round(drawW)}px`,
|
|
97
|
+
`height: ${Math.round(drawH)}px`,
|
|
98
|
+
`left: ${Math.round(offsetX)}px`,
|
|
99
|
+
`top: ${Math.round(offsetY)}px`,
|
|
100
|
+
'display: block',
|
|
101
|
+
'pointer-events: none',
|
|
102
|
+
];
|
|
103
|
+
img.style.removeProperty('object-fit');
|
|
104
|
+
img.style.removeProperty('object-position');
|
|
105
|
+
if (resizeMode?.type === 'none') {
|
|
106
|
+
styleParts[1] = `width: ${naturalW}px`;
|
|
107
|
+
styleParts[2] = `height: ${naturalH}px`;
|
|
108
|
+
}
|
|
109
|
+
img.setAttribute('style', styleParts.join('; ') + ';');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function applySubTextureScaling(
|
|
113
|
+
node: DOMNode,
|
|
114
|
+
img: HTMLImageElement,
|
|
115
|
+
srcPos: InstanceType<lng.TextureMap['SubTexture']>['props'] | null,
|
|
116
|
+
) {
|
|
117
|
+
if (!srcPos) return;
|
|
118
|
+
const regionW = node.props.srcWidth ?? srcPos.w;
|
|
119
|
+
const regionH = node.props.srcHeight ?? srcPos.h;
|
|
120
|
+
if (!regionW || !regionH) return;
|
|
121
|
+
const targetW = node.props.w || regionW;
|
|
122
|
+
const targetH = node.props.h || regionH;
|
|
123
|
+
if (targetW === regionW && targetH === regionH) return;
|
|
124
|
+
const naturalW = img.naturalWidth || regionW;
|
|
125
|
+
const naturalH = img.naturalHeight || regionH;
|
|
126
|
+
const scaleX = targetW / regionW;
|
|
127
|
+
const scaleY = targetH / regionH;
|
|
128
|
+
img.style.width = naturalW + 'px';
|
|
129
|
+
img.style.height = naturalH + 'px';
|
|
130
|
+
img.style.objectFit = 'none';
|
|
131
|
+
img.style.objectPosition = '0 0';
|
|
132
|
+
img.style.transformOrigin = '0 0';
|
|
133
|
+
const translateX = Math.round(-srcPos.x * scaleX);
|
|
134
|
+
const translateY = Math.round(-srcPos.y * scaleY);
|
|
135
|
+
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`;
|
|
136
|
+
img.style.setProperty('-webkit-transform', img.style.transform);
|
|
137
|
+
if (node.divBg) {
|
|
138
|
+
const styleEl = node.divBg.style;
|
|
139
|
+
if (
|
|
140
|
+
styleEl.maskImage ||
|
|
141
|
+
styleEl.webkitMaskImage ||
|
|
142
|
+
/mask-image:/.test(node.divBg.getAttribute('style') || '')
|
|
143
|
+
) {
|
|
144
|
+
img.style.display = 'none';
|
|
145
|
+
const maskW = Math.round(naturalW * scaleX);
|
|
146
|
+
const maskH = Math.round(naturalH * scaleY);
|
|
147
|
+
const maskPosX = translateX;
|
|
148
|
+
const maskPosY = translateY;
|
|
149
|
+
styleEl.setProperty?.('mask-size', `${maskW}px ${maskH}px`);
|
|
150
|
+
styleEl.setProperty?.('mask-position', `${maskPosX}px ${maskPosY}px`);
|
|
151
|
+
styleEl.setProperty?.('-webkit-mask-size', `${maskW}px ${maskH}px`);
|
|
152
|
+
styleEl.setProperty?.(
|
|
153
|
+
'-webkit-mask-position',
|
|
154
|
+
`${maskPosX}px ${maskPosY}px`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export function applyEasing(
|
|
160
|
+
easing: string | lng.TimingFunction,
|
|
161
|
+
progress: number,
|
|
162
|
+
): number {
|
|
163
|
+
if (isFunc(easing)) {
|
|
164
|
+
return easing(progress);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
switch (easing) {
|
|
168
|
+
case 'linear':
|
|
169
|
+
default:
|
|
170
|
+
return progress;
|
|
171
|
+
case 'ease-in':
|
|
172
|
+
return progress * progress;
|
|
173
|
+
case 'ease-out':
|
|
174
|
+
return progress * (2 - progress);
|
|
175
|
+
case 'ease-in-out':
|
|
176
|
+
return progress < 0.5
|
|
177
|
+
? 2 * progress * progress
|
|
178
|
+
: -1 + (4 - 2 * progress) * progress;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function interpolate(start: number, end: number, t: number): number {
|
|
182
|
+
return start + (end - start) * t;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function interpolateColor(start: number, end: number, t: number): number {
|
|
186
|
+
return (
|
|
187
|
+
(interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) |
|
|
188
|
+
(interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) |
|
|
189
|
+
(interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) |
|
|
190
|
+
interpolate(start & 0xff, end & 0xff, t)
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function interpolateProp(
|
|
195
|
+
name: string,
|
|
196
|
+
start: number,
|
|
197
|
+
end: number,
|
|
198
|
+
t: number,
|
|
199
|
+
): number {
|
|
200
|
+
return name.startsWith('color')
|
|
201
|
+
? interpolateColor(start, end, t)
|
|
202
|
+
: interpolate(start, end, t);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function compactString(input: string): string {
|
|
206
|
+
return input.replace(/\s*\n\s*/g, ' ');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// #region Renderer State Utils
|
|
210
|
+
|
|
211
|
+
export function isRenderStateInBounds(state: lng.CoreNodeRenderState): boolean {
|
|
212
|
+
return state === 4 || state === 8;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function nodeHasTextureSource(node: DOMNode): boolean {
|
|
216
|
+
const textureType = node.props.texture?.type;
|
|
217
|
+
return (
|
|
218
|
+
!!node.props.src ||
|
|
219
|
+
textureType === lng.TextureType.image ||
|
|
220
|
+
textureType === lng.TextureType.subTexture
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function normalizeBoundsMargin(
|
|
225
|
+
margin: number | [number, number, number, number] | null | undefined,
|
|
226
|
+
): [number, number, number, number] {
|
|
227
|
+
if (margin == null) return [0, 0, 0, 0];
|
|
228
|
+
if (typeof margin === 'number') {
|
|
229
|
+
return [margin, margin, margin, margin];
|
|
230
|
+
}
|
|
231
|
+
if (Array.isArray(margin) && margin.length === 4) {
|
|
232
|
+
return [margin[0] ?? 0, margin[1] ?? 0, margin[2] ?? 0, margin[3] ?? 0];
|
|
233
|
+
}
|
|
234
|
+
return [0, 0, 0, 0];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function computeRenderStateForNode(
|
|
238
|
+
node: DOMNode,
|
|
239
|
+
): lng.CoreNodeRenderState | null {
|
|
240
|
+
const stageRoot = node.stage.root as DOMNode | undefined;
|
|
241
|
+
if (!stageRoot || stageRoot === node) return null;
|
|
242
|
+
|
|
243
|
+
const rootWidth = stageRoot.props.w ?? 0;
|
|
244
|
+
const rootHeight = stageRoot.props.h ?? 0;
|
|
245
|
+
if (rootWidth <= 0 || rootHeight <= 0) return 4;
|
|
246
|
+
|
|
247
|
+
const rootLeft = stageRoot.absX;
|
|
248
|
+
const rootTop = stageRoot.absY;
|
|
249
|
+
const rootRight = rootLeft + rootWidth;
|
|
250
|
+
const rootBottom = rootTop + rootHeight;
|
|
251
|
+
|
|
252
|
+
const [marginTop, marginRight, marginBottom, marginLeft] =
|
|
253
|
+
normalizeBoundsMargin(
|
|
254
|
+
node.props.boundsMargin ?? node.stage.renderer.boundsMargin,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const width = node.props.w ?? 0;
|
|
258
|
+
const height = node.props.h ?? 0;
|
|
259
|
+
|
|
260
|
+
const left = node.absX;
|
|
261
|
+
const top = node.absY;
|
|
262
|
+
const right = left + width;
|
|
263
|
+
const bottom = top + height;
|
|
264
|
+
|
|
265
|
+
const expandedLeft = rootLeft - marginLeft;
|
|
266
|
+
const expandedTop = rootTop - marginTop;
|
|
267
|
+
const expandedRight = rootRight + marginRight;
|
|
268
|
+
const expandedBottom = rootBottom + marginBottom;
|
|
269
|
+
|
|
270
|
+
const intersectsBounds =
|
|
271
|
+
right >= expandedLeft &&
|
|
272
|
+
left <= expandedRight &&
|
|
273
|
+
bottom >= expandedTop &&
|
|
274
|
+
top <= expandedBottom;
|
|
275
|
+
|
|
276
|
+
if (!intersectsBounds) {
|
|
277
|
+
return 2;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const intersectsViewport =
|
|
281
|
+
right >= rootLeft &&
|
|
282
|
+
left <= rootRight &&
|
|
283
|
+
bottom >= rootTop &&
|
|
284
|
+
top <= rootBottom;
|
|
285
|
+
|
|
286
|
+
if (intersectsViewport) {
|
|
287
|
+
return 8;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return 4;
|
|
291
|
+
}
|