组件总览
容器内置 22 类标签。所有组件都由 Yoga 排版、NanoVG 绘制,不依赖 HTML。
未识别的标签会自动降级为 view。
| 标签 | 默认尺寸 | 可交互 | 事件 |
|---|---|---|---|
view | 自适应 | 否 | bindtap |
scroll-view | 自适应 | 可聚焦子节点 | bindtap |
text | 按文本测量 | 否 | bindtap |
rich-text | 宽度 100% | 否 | — |
image | 200 × 120rpx | 否 | bindtap |
icon | 48 × 48rpx | 否 | — |
button | 320 × 88rpx | 是 | bindtap |
navigator | 100% × 80rpx | 是 | bindtap + 路由 |
input | 100% × 80rpx | 是 | bindinput / bindconfirm |
textarea | 100% × 160rpx | 是 | bindinput / bindconfirm |
switch | 96 × 56rpx | 是 | bindchange |
checkbox | 48 × 48rpx | 是 | bindchange |
radio | 48 × 48rpx | 是 | bindchange |
slider | 100% × 80rpx | 是 | bindchange |
progress | 100% × 24rpx | 否 | — |
swiper | 100% × 300rpx | 自动轮播 | — |
swiper-item | 100% × 100% | 否 | — |
video | 100% × 360rpx | 是 | bindplay / bindpause / bindended / bindtimeupdate / binderror / bindloadedmetadata / bindwaiting / bindprogress / bindseekcomplete / bindfullscreenchange |
form / label / checkbox-group / radio-group / block | 容器 / 不渲染 | 否 | — |
view / scroll-view
默认 flex-direction: column。scroll-view 通过
scroll-y="{{true}}" 或 scroll-x="{{true}}" 开启滚动;
内容超出时由焦点导航自动滚动到可见区域。鼠标左键与触摸点击会命中渲染树,
触发对应节点的 bindtap 并沿祖先冒泡(catchtap 终止冒泡)。
鼠标滚轮暂未接入。
<scroll-view class="panel" scroll-y="{{true}}" style="height: 100%;">
<view class="card">...</view>
</scroll-view>
任何可交互节点都可以加 autofocus="{{true}}"(或 autofocus="true"):
该节点在树里新出现时,容器的摇杆焦点会自动移到它身上,常用于自定义弹窗的默认按钮。
焦点范围同时收敛到弹窗内部(该节点最近的绝对定位祖先),摇杆只在弹窗里循环,
不会跑到背后的列表上。
只在节点路径变化时抢一次焦点,用户随后在弹窗内移动焦点不会被拉回;
节点消失(关闭弹窗)后,焦点会还原到打开弹窗之前的那个节点(若它仍存在),
否则回到第一个可交互节点。
<view wx:if="{{modal}}" class="modal">
<view class="btn" bindtap="onCancel">取消</view>
<view class="btn primary" autofocus="{{true}}" bindtap="onConfirm">进入</view>
</view>
text / rich-text
text 支持 {{}} 插值与嵌套换行;默认继承父级颜色。
rich-text 只读取 nodes 字符串,当前降级为纯文本:剥离 HTML 标签、
还原常见实体,不渲染内联样式与图片。
<text>共 {{items.length}} 项</text>
<rich-text nodes="<b>标题</b>,正文"></rich-text>
image / icon
image 支持三种来源:包内路径(以 / 开头)、
启动器缓存的本地文件、以及 http(s):// 远程 URL(下载到缓存后解码)。
支持 scaleToFill(默认)、aspectFit、aspectFill。
远程图可通过 http-headers 附加请求头(如鉴权 Cookie),
格式为 "Name: value"(按第一个冒号分隔名值)。
lazy-load 为 true 时只有滚动到可视区域(含 200px 预取边距)才下载;
bindload 在解码成功时触发,binderror 在下载/解码失败时触发。
解码依赖 NanoVG/stb,目前不支持 webp。
<image src="/images/logo.png" mode="aspectFit" class="logo"/>
<!-- 远程鉴权图:带 Cookie 头预览 -->
<image src="{{url}}" http-headers="{{headers}}" mode="aspectFit"/>
icon 为原生绘制:type 可取
success / info / warn / waiting /
cancel / clear / search / download,
size 为 rpx 数值。
button / navigator
button 默认使用主题绿色,可用 class 或内联样式覆盖。
与 view 一样通过 bindtap 触发事件。
navigator 在激活时直接跳转:默认 navigateTo,
open-type 可取 redirect / reLaunch。
<button bindtap="onAdd">+1 计数</button>
<navigator url="/pages/detail/detail?from=list">打开详情</navigator>
input / textarea
激活后调用系统输入法。value 与 placeholder 支持数据绑定,
bindinput 在输入确认时触发,bindconfirm 同时触发。
textarea 目前复用单行输入法,不能真正换行;
多行仅体现为更高的默认高度。
<input class="input" value="{{nick}}" placeholder="昵称"
bindinput="onInput" bindconfirm="onConfirm"/>
switch / checkbox / radio
三者都是原生绘制,A 键切换状态并派发 change,事件对象
detail.value 为布尔值。选中状态保存在页面内,不会自动回写
data,需要在事件里自行 setData。
radio 通过 name 分组,同组只保留一个选中项。
<switch checked="{{enabled}}" bindchange="onSwitch"/>
<checkbox checked="{{accept}}" bindchange="onCheckbox"/>
<radio-group>
<radio name="plan" value="a" checked="{{plan==='a'}}" bindchange="onRadio"/>
<radio name="plan" value="b" checked="{{plan==='b'}}" bindchange="onRadio"/>
</radio-group>
slider / progress
slider 支持 min / max / step /
value;方向键左右调整,A 键按 step 递增,触发
change。progress 读取 percent(0–100),
可选 activeColor / backgroundColor。
<slider min="0" max="100" step="5" value="{{volume}}" bindchange="onSlider"/>
<progress percent="{{volume}}" activeColor="#22c55e"/>
swiper / swiper-item
swiper 只构建当前项的子树,autoplay 与
interval(毫秒)驱动自动轮播,底部绘制指示点。
暂不支持 bindchange、纵向轮播与手势滑动。
<swiper autoplay="{{true}}" interval="{{3000}}" class="swiper">
<swiper-item><view class="slide">第一屏</view></swiper-item>
<swiper-item><view class="slide">第二屏</view></swiper-item>
</swiper>
video
支持 src、poster、autoplay、loop、
muted、controls、http-headers、
object-fit(contain / cover / fill)、
initial-time(起播秒数)、playback-rate(倍速)、
volume(0-100)与 subtitle(外挂字幕地址,支持
smb://会话id/路径、sdmc:/... 与 HTTP(S))。
src 同样支持 sdmc:/... 本地绝对路径。
http-headers 会作为 mpv 的 http-header-fields 下发,
用于需要鉴权的直链(如带 Cookie 的 m3u8 / 视频直链),格式
"Cookie: a=b; c=d"。点击画面或按 A 键播放/暂停,
底部控制条显示进度、时间与状态。
播放通过 nxkit::VideoView(wiliwili 风格播放器,底层封装 libmpv):
- Switch 构建默认开启
NXKIT_WITH_MPV,可加载包内文件路径或 HTTP(S) 地址。 - 桌面构建默认关闭;需要安装 libmpv 后以
-DNXKIT_WITH_MPV=ON重新配置。 - 未启用 MPV 时只显示 poster 与占位控制条,点击播放会提示 “视频播放需要启用 libmpv 的构建”,不会伪造播放。
<video id="demoVideo" class="video" src="/videos/demo.mp4" poster="/images/logo.png"
autoplay="{{false}}" loop="{{true}}" muted="{{true}}" controls="{{true}}"
initial-time="{{60}}" playback-rate="{{1.5}}" volume="{{80}}"
subtitle="smb://s1/movies/demo.zh.srt"
bindplay="onVideoPlay" bindpause="onVideoPause"
bindended="onVideoEnded" bindtimeupdate="onVideoTime"
bindloadedmetadata="onMeta" bindwaiting="onWaiting" bindprogress="onProgress"
bindseekcomplete="onSeeked" bindfullscreenchange="onFullscreen"
binderror="onVideoError"/>
脚本侧通过 wx.createVideoContext(id) 控制播放:
const ctx = wx.createVideoContext('demoVideo');
ctx.play();
ctx.pause();
ctx.seek(30);
ctx.setPlaybackRate(1.5);
ctx.setVolume(80);
ctx.addSubtitle('https://example.com/demo.zh.srt');
ctx.requestFullScreen(); // 全屏铺满内容区,按 B 退出
ctx.stop();
事件语义:loadedmetadata 在时长/尺寸就绪时触发一次;
waiting 与 progress 对应缓冲开始/恢复;
seekcomplete 在 seek 结束后触发;fullscreenchange
带有 { fullScreen }。
全屏播放时视频位于最上层,普通节点会被画面盖住。给需要悬浮在画面上的子树加
data-overlay="true",容器会在视频直出之后单独合成这一层;
普通(非全屏)模式下该属性没有任何影响。
<view class="stage">
<video id="player" src="{{url}}" />
</view>
<view class="hud" data-overlay="true">
<text>{{channelName}}</text>
</view>
video;
视频直出层不参与常规 NanoVG 合成,被 scroll-view 裁剪或完全移出屏幕时暂停绘制
(全屏模式例外)。全屏下只有 data-overlay 子树、toast 与弹窗会显示在画面上层。
canvas
canvas 使用 retained-mode 绘制:脚本通过
wx.createCanvasContext(id) 拿到上下文,所有绘制指令先缓存,
调用 ctx.draw() 提交为当前帧,由 NanoVG 在页面合成时回放。
width / height 是绘图坐标系尺寸(默认 300×150),
实际显示尺寸由 WXSS 决定,容器按两者比例缩放。
默认样式为 width: 100%、height: 300rpx,背景为黑色。
<canvas id="game" class="stage" width="420" height="300"
bindtouchstart="onTouchStart" bindtouchmove="onTouchMove"
bindtouchend="onTouchEnd"></canvas>
const ctx = wx.createCanvasContext('game');
function frame(ts) {
ctx.setFillStyle('#0b0e12');
ctx.fillRect(0, 0, 420, 300);
ctx.setFillStyle('#10dc78');
ctx.fillRect(player.x, player.y, 32, 32);
ctx.draw(); // 提交当前帧
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
translate / scale / rotate;
暂不支持渐变、阴影、measureText 与离屏 canvas。
触摸与长按事件
任意节点都可以绑定 bindtouchstart / bindtouchmove /
bindtouchend / bindtouchcancel 与
bindlongpress(按住 500ms 触发)。
事件对象包含 touches / changedTouches
(每项含 clientX / clientY / pageX / pageY)
以及相对当前节点的 x / y,canvas 可直接换算绘图坐标。
使用 catchtouchstart 等 catch 前缀会阻止事件继续冒泡。
onTouchMove(e) {
const t = e.touches[0];
console.log('相对画布', e.x, e.y, '屏幕', t.clientX, t.clientY);
}
bindtap
(长按同理)。
form / label / checkbox-group / radio-group / block
这些标签只承担容器语义:form、label 与分组标签会正常排版子节点,
但不会汇总提交;block 不产生节点,只展开子节点。
表单提交建议在按钮的 bindtap 里手动读取页面状态。
client/apps/miniapp/source/miniapp_view.cpp 与 layout.cpp。