跳转到内容

内置 JSX 元素

在末语中,所有的 UI 都使用引擎提供的自定义 JSX 元素构建。这些元素不是 HTML 元素——它们直接对应引擎底层的渲染节点。

所有元素都支持以下属性:

属性类型默认值说明
labelstring节点标签(调试用,在开发工具中显示)
xnumber0X 坐标
ynumber0Y 坐标
anchor[number, number][0, 0]锚点位置(0~1 范围,决定坐标对齐点)
pivot[number, number][0, 0]旋转/缩放中心(0~1 范围)
scalenumber1整体缩放
scaleXnumber1水平缩放
scaleYnumber1垂直缩放
rotationnumber0旋转角度(角度制)
skewnumber0整体斜切
skewXnumber0水平斜切
skewYnumber0垂直斜切
visiblebooleantrue是否可见
tintstring"#FFFFFF"着色(CSS 颜色值)
opacitynumber1透明度(0~1)
interactivebooleanfalse是否响应交互事件
cursorMoyuCursor鼠标悬停时的光标样式

设置 interactive={true} 后,元素可以响应以下事件:

// 鼠标事件
onClick={(e: MouseEvent) => { ... }}
onMouseDown={(e: MouseEvent) => { ... }}
onMouseUp={(e: MouseEvent) => { ... }}
onMouseMove={(e: MouseEvent) => { ... }}
onMouseEnter={(e: MouseEvent) => { ... }}
onMouseLeave={(e: MouseEvent) => { ... }}
// 键盘事件
onKeyDown={(e: KeyboardEvent) => { ... }}
onKeyUp={(e: KeyboardEvent) => { ... }}
onKeyPress={(e: KeyboardEvent) => { ... }}
// 触摸事件
onTouchStart={(e: TouchEvent) => { ... }}
onTouchMove={(e: TouchEvent) => { ... }}
onTouchEnd={(e: TouchEvent) => { ... }}
onTouchCancel={(e: TouchEvent) => { ... }}

末语使用左上角为原点的坐标系,X 轴向右,Y 轴向下。坐标以舞台尺寸为基准(通常为 1920×1080),引擎会自动处理到不同窗口大小的缩放。


容器是最基础的布局元素,本身不渲染任何内容,用于组织和分组子元素。

<container label="角色层" x={0} y={0}>
<sprite src="characters/alice.png" />
<sprite src="characters/bob.png" />
</container>

容器支持所有通用属性。对容器设置的变换(位移、缩放、旋转等)会影响所有子元素。


用于显示图片。这是最常用的元素之一。

<sprite src="bg/classroom.png" />
<sprite src="ui/button.png" x={100} y={200} />
属性类型默认值说明
srcstring图片路径(相对于 assets/
mode"normal" | "nineslice""normal"渲染模式
area[number, number, number, number]裁剪区域(归一化坐标 0~1:[x0, y0, x1, y1]

九宫格模式用于制作可拉伸的 UI 元素(如按钮、面板背景),保持四个角不变形:

<sprite
src="ui/panel_bg.png"
mode="nineslice"
bounds={[20, 20, 20, 20]}
targetWidth={400}
targetHeight={300}
/>
属性类型说明
bounds[left, top, right, bottom]九宫格边距(像素)
nineSliceMode"stretch" | "repeat" | "mirror" | "blank"中间区域的填充方式
targetWidthnumber目标宽度
targetHeightnumber目标高度

用于渲染文本内容。支持多种打印效果。

<text
text="你好,世界!"
fontSize={32}
fillColor="#FFFFFF"
x={100}
y={100}
/>
属性类型默认值说明
textstring""文本内容
fontSizenumber字号
fillColorstring文本颜色(CSS 颜色值)
printMode"instant" | "typewriter" | "printer""instant"打印模式
printSpeednumber打印速度(typewriter: 字/秒,printer: 行/秒)
boxWidthnumber文本框宽度
boxHeightnumber文本框高度
lineHeightnumber行高倍数
indentnumber段首缩进(像素)
direction"horizontal" | "vertical""horizontal"排版方向
glyphGridSizenumber字形网格大小
// 描边效果
<text
text="带描边的文字"
stroke={true}
strokeColor="#000000"
strokeWidth={2}
/>
// 阴影效果
<text
text="带阴影的文字"
shadow={true}
shadowColor="#000000"
shadowOffsetX={2}
shadowOffsetY={2}
shadowBlur={4}
/>
属性类型默认值说明
strokebooleanfalse启用描边
strokeColorstring"#000000"描边颜色
strokeWidthnumber2描边宽度
shadowbooleanfalse启用阴影
shadowColorstring"#000000"阴影颜色
shadowOffsetXnumber0阴影 X 偏移
shadowOffsetYnumber0阴影 Y 偏移
shadowBlurnumber0阴影模糊半径
shadowWidthnumber0阴影宽度

<text> 元素在打印过程中会触发事件:

<text
text="正在打字……"
printMode="typewriter"
printSpeed={30}
onStart={() => console.log('开始打印')}
onProgress={(progress) => console.log(`进度: ${progress}`)}
onFinish={() => console.log('打印完成')}
/>
事件类型说明
onStart() => void开始打印时触发
onProgress(progress: number) => void打印进度(0~1)
onFinish() => void打印完成时触发

<text> 元素的文本内容支持内联富文本标签:

<text text="这是<fillColor=#E7931C>橙色文字</fillColor>,<bold>加粗</bold>,<underline>下划线</underline>。" />

裁剪容器,子元素只在指定范围内可见。

<clip x={100} y={100} width={400} height={300}>
<sprite src="large_image.png" />
</clip>
属性类型说明
widthnumber裁剪区域宽度
heightnumber裁剪区域高度

对子元素应用视觉滤镜效果。

<filter filters={[{ type: 'blur', radius: 4 }]}>
<sprite src="bg/photo.png" />
</filter>
属性类型说明
filtersFilterKind[]滤镜列表(可叠加多个)

FilterKind 是一个联合类型,每种滤镜的结构如下:

type参数说明
'blur'radius: number, continuous?: boolean高斯模糊
'blur-perfect'radius: number精确模糊(质量更高,性能较低)
'brightness'amount: number亮度(1 为原始值)
'contrast'amount: number对比度(1 为原始值)
'saturation'amount: number饱和度(1 为原始值)
'hue-rotate'degrees: number色相旋转(角度)
'grayscale'amount: number灰度(0~1)
'sepia'amount: number复古效果(0~1)
'invert'amount: number反色(0~1)

对元素后方的已有画面内容应用滤镜,类似 CSS 的 backdrop-filter

<backdrop
filters={[{ type: 'blur', radius: 8 }]}
width={1920}
height={1080}
/>
属性类型说明
filtersFilterKind[]滤镜列表,结构同 <filter>
widthnumber区域宽度
heightnumber区域高度

播放序列帧动画(APNG 或 WebP 动画格式)。

<animation src="effects/fire.apng" />
属性类型说明
srcstring动画文件路径(APNG 或 WebP)
area[number, number, number, number]裁剪区域(归一化坐标)
format"apng" | "webp"动画格式

播放视频文件,支持 VP9(WebM/MP4)和 AV1(MP4)编码格式,支持音视频同步。视频加载完成后默认自动开始播放。

<video src="video/opening.mp4" />
{/* 循环播放背景视频,静音 */}
<video src="video/bg_loop.webm" loop muted />
{/* 控制音量,不自动播放 */}
<video src="video/scene.mp4" volume={0.5} autoPlay={false} />
属性类型默认值说明
srcstring视频文件路径(相对于 assets/
loopbooleanfalse是否循环播放
autoPlaybooleantrue加载完成后是否自动开始播放
volumenumber1音量(0~1)
mutedbooleanfalse是否静音
属性类型说明
onEnded() => void视频播放完毕时触发(仅在非循环模式下)
onStateChange(state: string) => void播放状态变化时触发,state 为 "idle" "loading" "playing" "paused" "stopped" "ended" "error"

以下是一个实际的 UI 组合示例,展示如何用这些元素构建界面:

function SimpleDialog() {
return (
<container label="对话框">
{/* 模糊背景 */}
<backdrop filters={[{ type: 'blur', radius: 6 }]} width={1920} height={1080} />
{/* 对话框面板 */}
<sprite
src="ui/dialog_bg.png"
mode="nineslice"
bounds={[24, 24, 24, 24]}
targetWidth={800}
targetHeight={400}
x={560}
y={340}
>
{/* 标题文字 */}
<text
text="提示"
fontSize={36}
fillColor="#FFFFFF"
x={40}
y={30}
/>
{/* 内容文字 */}
<text
text="确定要退出游戏吗?"
fontSize={28}
fillColor="#CCCCCC"
x={40}
y={120}
/>
</sprite>
</container>
);
}