Skip to content

Masking & SafeArea

UIMask

Clips all child entities to the parent’s UIRect bounds. The entity with UIMask itself is not clipped — only its descendants.

Required components: The masked entity must have Transform and UIRect.

Properties

PropertyTypeDefaultDescription
enabledbooleantrueWhether clipping is active
modeMaskModeScissorMasking technique (see below)

MaskMode

ModeValueDescription
Scissor0GPU scissor test — fast, axis-aligned rectangles only
Stencil1Stencil buffer — supports arbitrary shapes and nesting

When to use which:

  • Scissor — Use for simple rectangular containers like scroll views, panels, and card lists. Faster but limited to axis-aligned rectangles.
  • Stencil — Use when you need non-rectangular masks (circular avatars, custom shapes) or when nesting masks inside masks (supports up to 255 levels). Slightly more expensive.

Setup in Editor

  1. Select or create a UI entity with UIRect (this defines the clipping bounds)
  2. Add the UIMask component in the Inspector
  3. Choose Scissor or Stencil mode
  4. Any child entities added under this entity will be clipped to its bounds

Setup in Code

import { UIMask } from 'esengine';
// Create a mask container
const panel = world.spawn();
world.insert(panel, UIRect, {
anchorMin: { x: 0.1, y: 0.1 },
anchorMax: { x: 0.9, y: 0.9 },
});
// mode: 0 = Scissor, 1 = Stencil
world.insert(panel, UIMask, { enabled: true, mode: 0 });
// Child content — automatically clipped
const content = world.spawn();
world.setParent(content, panel);
world.insert(content, Sprite, { texture: myTexture });

Nested Masks

Scissor masks intersect with parent scissors — a child scissor mask produces the overlapping rectangle of both.

Stencil masks support true nesting: each level gets its own stencil ref value, and children are tested against their parent’s stencil. Up to 255 nesting levels are supported.

Root (UIMask Stencil)
├── Header (clipped to Root)
└── Body (UIMask Stencil) ← nested mask
├── Item 1 (clipped to Body ∩ Root)
└── Item 2 (clipped to Body ∩ Root)

SafeArea

Automatically adapts a UI element’s layout to device safe areas (notches, rounded corners, status bars). The component adjusts UIRect.offsetMin and UIRect.offsetMax to shrink the element away from unsafe edges.

Required components: The entity must have UIRect.

Properties

PropertyTypeDefaultDescription
applyTopbooleantrueShrink from top edge (status bar, notch)
applyBottombooleantrueShrink from bottom edge (home indicator)
applyLeftbooleantrueShrink from left edge (landscape notch)
applyRightbooleantrueShrink from right edge (landscape notch)

How It Works

  1. The SafeAreaPlugin reads device insets at startup and on window resize
  2. Insets are scaled from screen pixels to world units based on canvas aspect ratio
  3. Each frame (PreUpdate), the plugin applies the scaled insets to UIRect.offsetMin / UIRect.offsetMax for each entity with SafeArea

Typical Usage

Create a full-screen container with SafeArea and place all UI content inside it:

Canvas (full screen)
└── SafeArea Container (UIRect: anchors 0,0 → 1,1) ← add SafeArea here
├── HUD
├── Score
└── Controls

In the editor: Add SafeArea to your root UI panel. Set anchors to stretch (0,0 → 1,1). All children will be laid out within the safe area.

In code:

import { SafeArea } from 'esengine';
const safeContainer = world.spawn();
world.insert(safeContainer, UIRect, {
anchorMin: { x: 0, y: 0 },
anchorMax: { x: 1, y: 1 },
});
world.insert(safeContainer, SafeArea, {
applyTop: true,
applyBottom: true,
applyLeft: true,
applyRight: true,
});

Selective Edges

You may want SafeArea on some edges but not others. For example, a bottom navigation bar only needs the bottom safe area:

world.insert(navBar, SafeArea, {
applyTop: false,
applyBottom: true,
applyLeft: true,
applyRight: true,
});

Platform Support

PlatformHow Insets are Detected
WebCSS env(safe-area-inset-*) via computed style variables --sat, --sab, --sal, --sar
WeChat MiniGamewx.getSystemInfoSync() — reads safeArea field, auto-updates on window resize
DesktopReturns zero insets (no safe area concerns)

Plugin Registration

Both uiMaskPlugin and safeAreaPlugin are registered by the engine automatically. No manual setup is needed.

Next Steps