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
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Whether clipping is active |
mode | MaskMode | Scissor | Masking technique (see below) |
MaskMode
| Mode | Value | Description |
|---|---|---|
Scissor | 0 | GPU scissor test — fast, axis-aligned rectangles only |
Stencil | 1 | Stencil 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
- Select or create a UI entity with
UIRect(this defines the clipping bounds) - Add the
UIMaskcomponent in the Inspector - Choose
ScissororStencilmode - Any child entities added under this entity will be clipped to its bounds
Setup in Code
import { UIMask } from 'esengine';
// Create a mask containerconst 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 = Stencilworld.insert(panel, UIMask, { enabled: true, mode: 0 });
// Child content — automatically clippedconst 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
| Property | Type | Default | Description |
|---|---|---|---|
applyTop | boolean | true | Shrink from top edge (status bar, notch) |
applyBottom | boolean | true | Shrink from bottom edge (home indicator) |
applyLeft | boolean | true | Shrink from left edge (landscape notch) |
applyRight | boolean | true | Shrink from right edge (landscape notch) |
How It Works
- The
SafeAreaPluginreads device insets at startup and on window resize - Insets are scaled from screen pixels to world units based on canvas aspect ratio
- Each frame (PreUpdate), the plugin applies the scaled insets to
UIRect.offsetMin/UIRect.offsetMaxfor each entity withSafeArea
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 └── ControlsIn 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
| Platform | How Insets are Detected |
|---|---|
| Web | CSS env(safe-area-inset-*) via computed style variables --sat, --sab, --sal, --sar |
| WeChat MiniGame | wx.getSystemInfoSync() — reads safeArea field, auto-updates on window resize |
| Desktop | Returns 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
- UI Layout — anchors, flex, and grid layout
- Widgets — buttons, sliders, toggles, and more
- WeChat MiniGame — platform-specific considerations