跳到主要内容

常用控件

界面组件是基础积木。本页将其组合为常用控件,每个示例给出可在编辑器或代码中构建的层级,以及所需行为代码。

常用控件

进度条与血条​

health bar 图像元素:背景,300 × 30
└── track 组元素:拉伸锚点,边距为 4,定义填充可用空间
└── fill 图像元素:拉伸锚点,边距为 0,使用进度条颜色

填充元素的右锚点随 0 到 1 的值变化,使其占轨道对应比例,并由轨道在条内保留边框:

function setHealth(value) {
fill.element.anchor = new pc.Vec4(0, 0, value, 1);
}
setHealth(0.7);

这种方法会挤压填充纹理。若要裁切而非压缩,可将填充锚点和轴心都设在轨道左侧,同时修改宽度与纹理矩形:fill.element.width = value * 292,其中 292 为轨道宽;再设置 fill.element.rect = new pc.Vec4(0, 0, value, 1)。详见进度条与圆形加载条教程。

滑块​

滑块比轨道短的滚动条就是滑块控件,拖动时 value 在 0 到 1 之间变化,垂直方向的 0 位于顶部。

volume 图像元素:轨道,300 × 20,并带水平滚动条组件
└── handle 启用输入的图像元素:沿轨道高度锚定
volume.scrollbar.handleSize = 0.1;
volume.scrollbar.value = 0.8;
volume.scrollbar.on('set:value', (value) => {
app.systems.sound.volume = value;
});

开关与单选组​

开关是通过外观显示状态的按钮:

music toggle 启用输入的图像元素,并带按钮组件
└── check 图像元素:勾选标记,开关启用时显示
let musicOn = true;
musicToggle.button.on('click', () => {
musicOn = !musicOn;
musicToggle.findByName('check').enabled = musicOn;
});

单选组由这类按钮组成的组元素构成,同一时间仅开启一个:

const options = difficulty.children;
for (const option of options) {
option.button.on('click', () => {
for (const other of options) {
other.findByName('check').enabled = other === option;
}
});
}

模态对话框覆盖其余界面,并在关闭前阻止底层输入:

screen
├── hud …
└── dialog 组元素:拉伸锚点
├── backdrop 图像元素:拉伸锚点,黑色,不透明度 0.6,启用输入
└── panel 启用输入的图像元素:对话框,包含文字与按钮

对话框位于层级末尾,因此显示在最上方并优先接收输入。背景填满屏幕且启用输入,捕获面板之外的点击,使下方 HUD 无法接收。面板也应启用输入,避免空白区域点击传到背景。要同时隔离游戏输入,应停止事件传播,详见防止界面输入传到游戏。

function openDialog() {
dialog.enabled = true;
}
function closeDialog() {
dialog.enabled = false;
}

// Clicking outside the panel closes the dialog
dialog.findByName('backdrop').element.on('click', closeDialog);

要覆盖多个屏幕,可将对话框放在独立且优先级更高的屏幕中。

工具提示​

工具提示是在指针悬停元素时显示的面板:

// The tooltip is a child of the icon, anchored above it
icon.element.useInput = true;
icon.element.on('mouseenter', () => {
tooltip.enabled = true;
});
icon.element.on('mouseleave', () => {
tooltip.enabled = false;
});

子项先于父项后方的同级项绘制,可能被覆盖。要让提示置顶,应将其作为屏幕最后一个子项,显示时再移到目标元素处。触摸屏没有悬停,可改为长按显示,或将信息直接放在屏幕上。

动态列表​

排行榜或背包等由数据生成的列表,可以使用布局组,按数据创建子项。可能超出区域时,放入滚动视图。

function showScores(scores) {
// Remove the old rows, then add one for each score
for (const row of [...list.children]) {
row.destroy();
}
for (const { name, score } of scores) {
const row = new pc.Entity(name);
row.addComponent('element', {
type: pc.ELEMENTTYPE_TEXT,
fontAsset: font.id,
text: `${name} ${score}`,
fontSize: 28
});
list.addChild(row);
}
}

showScores([{ name: 'Ada', score: 1250 }, { name: 'Grace', score: 990 }]);

拖放​

ElementDragHelper 可使元素在拖动期间跟随指针。元素需要启用输入,应用需要鼠标或触摸设备:

const drag = new pc.ElementDragHelper(card.element);
const home = card.getLocalPosition().clone();

// The bounds of an element, in CSS pixels from the top-left of the canvas
const bounds = (entity) => {
const [bottomLeft, , topRight] = entity.element.canvasCorners;
return { left: bottomLeft.x, right: topRight.x, top: topRight.y, bottom: bottomLeft.y };
};

drag.on('drag:end', () => {
const a = bounds(card);
const b = bounds(slot);
const overSlot = a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;
// Snap the card into the slot, or back to where it came from
if (overSlot) {
card.setPosition(slot.getPosition());
} else {
card.setLocalPosition(home);
}
});

第二参数传入 'x' 或 'y',可限制单轴拖动。开始和持续拖动时分别触发 drag:start 与 drag:move。

拖放

文本框​

界面系统没有文本输入元素。可将 HTML <input> 覆盖到画布上,获得浏览器键盘、选区、自动补全与无障碍支持。详见 HTML 与 CSS;界面文本输入教程展示与画布界面风格一致的文本框。

界面动画​

通过实体位置、旋转、缩放,以及元素颜色、不透明度、锚点和边距制作动画,可每帧小幅修改,或使用补间库:

// Slide a panel in from the left over half a second
let t = 0;
const handle = app.on('update', (dt) => {
t = Math.min(t + dt / 0.5, 1);
const eased = 1 - Math.pow(1 - t, 3);
panel.setLocalPosition(pc.math.lerp(-600, 0, eased), 0, 0);
if (t === 1) handle.off();
});

不透明度不传递给子项,因此淡出面板需遍历其下所有元素:

function setOpacity(entity, opacity) {
if (entity.element && entity.element.type !== pc.ELEMENTTYPE_GROUP) {
entity.element.opacity = opacity;
}
for (const child of entity.children) {
setOpacity(child, opacity);
}
}

示例将所有元素设为同一不透明度。原始值不同则应先保存。按钮通过 fadeDuration 在状态颜色间过渡;补间库详见 Tween。

加载画面与场景切换​

  • 加载画面。 编辑器项目在场景和界面都未加载前显示加载画面,因此使用 HTML 与 CSS,详见加载画面。
  • 场景切换。 属于场景的界面会随场景销毁。要保留 HUD,应将新场景层级叠加加载到旁边,而非替换全部内容。详见加载场景与切换场景教程。

触屏摇杆​

屏幕摇杆和触屏按钮本质上是读取触摸事件的元素。触屏摇杆教程构建了可复用的双摇杆布局。

另请参阅​