@dcl/react-ecs
Version:
Decentraland ECS
178 lines (177 loc) • 5.69 kB
JavaScript
import { engine, UiCanvasInformation as engineUiCanvasInformation } from '@dcl/ecs';
import { parseUiBackground } from './uiBackground';
import { parseUiTransform } from './uiTransform';
let uiScaleFactor = 1;
let uiScaleOwner = undefined;
const ZERO_INSETS = { top: 0, left: 0, right: 0, bottom: 0 };
let screenInsetArea = { ...ZERO_INSETS };
let screenInsetAreaOwner = undefined;
let interactableArea = { ...ZERO_INSETS };
let interactableAreaOwner = undefined;
/**
* @internal
*/
export function parseProps(props) {
const { uiTransform, uiBackground, ...otherProps } = props;
const uiTransformProps = parseUiTransform(uiTransform);
const uiBackgroundProps = uiBackground ? { uiBackground: parseUiBackground(uiBackground) } : undefined;
return {
...otherProps,
uiTransform: uiTransformProps,
...uiBackgroundProps
};
}
/**
* @internal
*/
export function getScaleAndUnit(scaleUnit) {
if (typeof scaleUnit === 'number') {
return [scaleUnit, 'vw'];
}
const value = Number(scaleUnit.slice(0, -2));
if (scaleUnit.endsWith('vh'))
return [value, 'vh'];
if (scaleUnit.endsWith('vw'))
return [value, 'vw'];
return [NaN, 'vw'];
}
/**
* @internal
*/
export function scaleOnDim(scale, dim, pxRatio) {
return (dim / 100) * (scale / pxRatio);
}
/**
* @internal
*/
export function getScaleCtx(_engine = engine) {
const UiCanvasInformation = _engine.getComponent(engineUiCanvasInformation.componentId);
const canvasInfo = UiCanvasInformation.getOrNull(_engine.RootEntity);
if (!canvasInfo)
return undefined;
const { width, height, devicePixelRatio } = canvasInfo;
return { width, height, ratio: devicePixelRatio };
}
/**
* @internal
*/
export function getUiScaleFactor() {
return uiScaleFactor;
}
/**
* Sets the global UI scale factor.
*
* The `owner` symbol implements a cooperative reset-protection scheme shared
* with {@link resetUiScaleFactor}:
* - Writes always succeed — last writer claims ownership (the most recent
* `owner` passed to `set` is the one allowed to `reset`).
* - Resets from a non-matching owner are ignored, so a stale system can't
* stomp the active scale while another system is driving it.
* - A reset called without an owner always wins (used by tests / teardown).
*
* @internal
*/
export function setUiScaleFactor(nextScale, owner) {
if (!Number.isFinite(nextScale) || nextScale < 0)
return;
if (owner) {
uiScaleOwner = owner;
}
uiScaleFactor = nextScale;
}
/**
* @internal
*/
export function resetUiScaleFactor(owner) {
// No-op for non-owners (see ownership rules on `setUiScaleFactor`).
// A reset with no owner always wins — used by tests and teardown.
if (owner && uiScaleOwner !== owner)
return;
uiScaleOwner = undefined;
uiScaleFactor = 1;
}
/**
* @internal
*/
export function getScreenInsetArea() {
return { ...screenInsetArea };
}
/**
* Sets the global screen inset area.
*
* The `owner` symbol implements a cooperative reset-protection scheme shared
* with {@link resetScreenInsetArea}:
* - Writes always succeed — last writer claims ownership (the most recent
* `owner` passed to `set` is the one allowed to `reset`).
* - Resets from a non-matching owner are ignored, so a stale system can't
* stomp the active insets while another system is driving them.
* - A reset called without an owner always wins (used by tests / teardown).
*
* @internal
*/
export function setScreenInsetArea(next, owner) {
if (owner) {
screenInsetAreaOwner = owner;
}
screenInsetArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom };
}
/**
* @internal
*/
export function resetScreenInsetArea(owner) {
// No-op for non-owners (see ownership rules on `setScreenInsetArea`).
// A reset with no owner always wins — used by tests and teardown.
if (owner && screenInsetAreaOwner !== owner)
return;
screenInsetAreaOwner = undefined;
screenInsetArea = { ...ZERO_INSETS };
}
/**
* @internal
*/
export function getInteractableArea() {
return { ...interactableArea };
}
/**
* Sets the global interactable area.
*
* The `owner` symbol implements a cooperative reset-protection scheme shared
* with {@link resetInteractableArea}:
* - Writes always succeed — last writer claims ownership (the most recent
* `owner` passed to `set` is the one allowed to `reset`).
* - Resets from a non-matching owner are ignored, so a stale system can't
* stomp the active area while another system is driving it.
* - A reset called without an owner always wins (used by tests / teardown).
*
* @internal
*/
export function setInteractableArea(next, owner) {
if (owner) {
interactableAreaOwner = owner;
}
interactableArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom };
}
/**
* @internal
*/
export function resetInteractableArea(owner) {
// No-op for non-owners (see ownership rules on `setInteractableArea`).
// A reset with no owner always wins — used by tests and teardown.
if (owner && interactableAreaOwner !== owner)
return;
interactableAreaOwner = undefined;
interactableArea = { ...ZERO_INSETS };
}
/**
* @internal
*/
export function calcOnViewport(value, ctx = getScaleCtx()) {
const [scale, unit] = getScaleAndUnit(value);
if (!ctx)
return scale;
const { height, width, ratio } = ctx;
if (unit === 'vh')
return scaleOnDim(scale, height, ratio);
// by default, we scale by 'vw' (width)
return scaleOnDim(scale, width, ratio);
}