docs: 组件使用指南 & 部分文档微调

This commit is contained in:
unanmed 2025-08-28 17:47:23 +08:00
parent d39e2c2159
commit 1169db5dfd
22 changed files with 913 additions and 108 deletions

View File

@ -20,7 +20,7 @@ export default defineConfig({
outline: [2, 3],
nav: [
{ text: '主页', link: '/' },
{ text: '指南', link: '/guide/diff' },
{ text: '指南', link: '/guide/quick-start' },
{ text: 'API', link: '/api/' },
{ text: '错误代码', link: '/logger/' }
],
@ -39,12 +39,17 @@ export default defineConfig({
text: 'UI 系统',
collapsed: false,
items: [
{ text: 'UI 编写', link: '/guide/ui' },
{ text: 'UI 优化', link: '/guide/ui-perf' },
{ text: 'UI 系统', link: '/guide/ui-system' },
{ text: 'UI 元素', link: '/guide/ui-elements' },
{ text: 'UI 常见问题', link: '/guide/ui-faq' },
{ text: '未来规划', link: '/guide/ui-future' }
{ text: '快速浏览', link: '/guide/ui/' },
{ text: '编写 UI', link: '/guide/ui/ui' },
{ text: 'UI 元素', link: '/guide/ui/elements' },
{
text: '组件使用指南',
link: '/guide/ui/component'
},
{ text: '优化性能', link: '/guide/ui/perf' },
{ text: 'UI 系统', link: '/guide/ui/system' },
{ text: '常见问题', link: '/guide/ui/faq' },
{ text: '未来规划', link: '/guide/ui/future' }
]
},
{

View File

@ -7,7 +7,22 @@
## 接口定义
```typescript
interface GraphicBaseProps extends BaseProps {
interface ILineProperty {
/** 线宽 */
lineWidth: number;
/** 线的虚线设置 */
lineDash?: number[];
/** 虚线偏移量 */
lineDashOffset?: number;
/** 线的连接样式 */
lineJoin: CanvasLineJoin;
/** 线的顶端样式 */
lineCap: CanvasLineCap;
/** 线的斜接限制当连接为miter类型时可填默认为10 */
miterLimit: number;
}
interface GraphicBaseProps extends BaseProps, Partial<ILineProperty> {
/** 是否填充(默认 false */
fill?: boolean;
/** 是否描边(默认 false */
@ -29,15 +44,21 @@ interface GraphicBaseProps extends BaseProps {
## 核心属性说明
| 属性 | 类型 | 默认值 | 说明 |
| --------------- | ----------------------------------------------- | ----------- | -------------------------------------------------------------- |
| `fill` | `boolean` | `false` | 启用填充(需设置 `fillStyle` |
| `stroke` | `boolean` | `false` | 启用描边(需设置 `strokeStyle``strokeWidth` |
| `strokeAndFill` | `boolean` | `false` | 强制先描边后填充(覆盖 `fill``stroke` 的设置) |
| `fillRule` | `"nonzero"` \| `"evenodd"` | `"evenodd"` | 填充路径计算规则(影响复杂图形的镂空效果) |
| `fillStyle` | `string` \| `CanvasGradient` \| `CanvasPattern` | - | 填充样式(支持 CSS 颜色、渐变对象等) |
| `strokeStyle` | `string` \| `CanvasGradient` \| `CanvasPattern` | - | 描边样式 |
| `actionStroke` | `boolean` | `false` | 设为 `true` 时,交互事件仅响应描边区域(需配合 `stroke` 使用) |
| 属性 | 类型 | 默认值 | 说明 |
| ---------------- | ----------------------------------------------- | ----------- | -------------------------------------------------------------- |
| `fill` | `boolean` | `false` | 启用填充 |
| `stroke` | `boolean` | `false` | 启用描边 |
| `strokeAndFill` | `boolean` | `false` | 先描边后填充 |
| `fillRule` | `'nonzero'` \| `'evenodd'` | `'evenodd'` | 填充路径环绕原则 |
| `fillStyle` | `string` \| `CanvasGradient` \| `CanvasPattern` | - | 填充样式 |
| `strokeStyle` | `string` \| `CanvasGradient` \| `CanvasPattern` | - | 描边样式 |
| `actionStroke` | `boolean` | `false` | 设为 `true` 时,交互事件仅响应描边区域(需配合 `stroke` 使用) |
| `lineWidth` | `number` | `2` | 设置描边的线宽 |
| `lineDash` | `number[]` | `[]` | 设置虚线样式 |
| `lineDashOffset` | `number` | `0` | 虚线样式偏移量 |
| `lineJoin` | `string` | `bevel` | 线的连接方式 |
| `lineCap` | `string` | `butt` | 线的末端样式 |
| `miterLimit` | `number` | `10` | 当使用 `miter` 连接方式时,其最大斜接限制 |
---
@ -55,8 +76,8 @@ interface GraphicBaseProps extends BaseProps {
**效果**
- 200x150 矩形
- 无描边效果
- 200x150 矩形
- 无描边效果
---
@ -67,15 +88,15 @@ interface GraphicBaseProps extends BaseProps {
loc={[400, 200, 180, 120]}
stroke
strokeStyle="rgba(0,0,0,0.8)"
strokeWidth={4}
lineWidth={4}
actionStroke // 点击时仅描边区域响应
/>
```
**交互特性**
- 4px 黑色半透明描边
- 鼠标悬停在描边区域才会触发事件
- 4px 黑色半透明描边
- 鼠标悬停在描边区域才会触发事件
---
@ -88,7 +109,7 @@ interface GraphicBaseProps extends BaseProps {
stroke
fillStyle="#ffe66d"
strokeStyle="#2d3436"
strokeWidth={2}
lineWidth={2}
/>
```
@ -99,7 +120,7 @@ interface GraphicBaseProps extends BaseProps {
---
### 示例 4强制先描边后填充
### 示例 4先描边后填充
```tsx
<g-rect
@ -107,7 +128,7 @@ interface GraphicBaseProps extends BaseProps {
strokeAndFill
fillStyle="#a29bfe"
strokeStyle="#6c5ce7"
strokeWidth={8}
lineWidth={8}
/>
```
@ -138,7 +159,7 @@ const leave = () => void (hovered.value = false);
fillStyle="#ffffff"
stroke={hovered.value}
strokeStyle="#e84393"
strokeWidth={3}
lineWidth={3}
onEnter={enter}
onLeave={leave}
/>;

View File

@ -8,18 +8,27 @@
图标比例固定,会自动根据传入的长宽缩放。
图标继承所有图形参数,参考[此文档](../motajs-render-vue/GraphicBaseProps.md)
## 图标列表
- `RollbackIcon`: 回退图标
- `RetweenIcon`: 回收图标
- `ViewMapIcon`: 浏览地图图标
- `DanmakuIcon`: 弹幕图标
- `ReplayIcon`: 回放图标
- `numpadIcon`: 数字键盘图标
- `PlayIcon`: 开始播放图标
- `PauseIcon`: 暂停播放图标
- `DoubleArrow`: 双箭头图标(向右)
- `StepForward`: 单步向前图标
- `RollbackIcon`: 回退图标
- `RetweenIcon`: 回收图标
- `ViewMapIcon`: 浏览地图图标
- `DanmakuIcon`: 弹幕图标
- `ReplayIcon`: 回放图标
- `numpadIcon`: 数字键盘图标
- `PlayIcon`: 开始播放图标
- `PauseIcon`: 暂停播放图标
- `DoubleArrow`: 双箭头图标(向右)
- `StepForward`: 单步向前图标
- `SoundVolume`: 音量图标
- `Fullscreen`: 全屏图标
- `ExitFullscreen`: 退出全屏图标
- `ArrowLeftTailless`: 无尾巴左箭头图标
- `ArrowRightTailless`: 无尾巴右箭头图标
- `ArrowUpTailless`: 无尾巴上箭头图标
- `ArrowDownTailless`: 无尾巴下箭头图标
## 使用示例

View File

@ -4,10 +4,10 @@
## 组件特性
- **虚拟滚动**:自动裁剪可视区域外元素
- **双模式支持**:垂直/水平滚动(默认垂直)
- **性能优化**:动态计算可视区域,支持万级元素流畅滚动
- **编程控制**:支持精准定位滚动位置
- **虚拟滚动**:自动裁剪可视区域外元素
- **双模式支持**:垂直/水平滚动(默认垂直)
- **性能优化**:动态计算可视区域,支持万级元素流畅滚动
- **编程控制**:支持精准定位滚动位置
---
@ -116,3 +116,14 @@ export const MyCom = defineComponent(() => {
{page => renderChunk(data.slice(page * 50, (page + 1) * 50))}
</Page>
```
### 2. 缓存建议
如果子元素数量较多,建议给 `Scroll` 组件的所有子元素添加 `nocache` 标记:
```tsx {2}
<Scroll>
<item nocache />
{/* 更多内容 */}
</Scroll>
```

View File

@ -18,17 +18,17 @@ lang: zh-CN
## 主要差异
- 开发语言换为 TypeScript可以享受到完整的类型支持
- 使用全新的 UI 编写方式,速度快,效率高
- 模块化,可以使用 ES6 模块化语法
- 移除插件系统,可以自定义代码目录结构,更加自由
- 优化渲染端client 端与数据端data 端)的通讯,渲染端现在可以直接引用数据端,不过数据端还不能直接引用渲染端
- 开发语言换为 TypeScript可以享受到完整的类型支持
- 使用全新的 UI 编写方式,速度快,效率高
- 模块化,可以使用 ES6 模块化语法
- 移除插件系统,可以自定义代码目录结构,更加自由
- 优化渲染端client 端与数据端data 端)的通讯,渲染端现在可以直接引用数据端,不过数据端还不能直接引用渲染端
## 差异内容
相比于 2.10.3 及 2.A有如下改动
- [系统说明](./system)
- [UI 编写](./ui)
- [UI 系统](./ui-system)
- [音频系统](./audio)
- [系统说明](./system)
- [UI 编写](./ui)
- [UI 系统](./ui/system)
- [音频系统](./audio)

View File

@ -2,6 +2,10 @@
2.B 提供了专门的动画接口,允许你用短短几行就可以做出一个效果不错的动画。本节主要讲述的是 UI 中的动画。
:::warning
`mutate-animate` 库在未来会重构,可能会修改引入方式,但整体逻辑不会怎么变。
:::
## 定义动画属性
我们以一个自定义 UI 为例,来讲述如何编写一段动画。自定义 UI 参考[此指南](./new-ui.md)和[此教程](../../guide/ui.md)。

View File

@ -179,6 +179,98 @@ export const MyCom = defineComponent(props => {
});
```
## 拓展-输入框
与选择框、确认框类似,只不过允许玩家输入一段内容,然后返回给程序。使用 `getInput` 来让玩家输入字符串,使用 `getInputNumber` 来让玩家输入数字。
输入框包含一个确认键和取消键,玩家点击确认键时会将输入结果返回,而如果点击了取消,那么会返回空字符串或 `NaN`
### 输入字符串
使用 `getInput` 接口:
```tsx
import { getInput } from '../components';
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(props => {
const click = async () => {
// 调用接口,并等待执行完毕,获取异步返回值
const inputData = await getInput(
props.controller, // UI 控制器
'请输入一句话', // 显示的文字
[240, 240, void 0, void 0, 0.5, 0.5], // 输入框位置
240, // 输入框宽度
// 其他参数配置,参考 API 文档
{
// 例如设置一个占位符
input: {
placeholder: '输入一句话'
}
}
);
if (inputData.length === 0) {
// 如果用户没有输入任何内容或点了取消
} else {
// 如果用户输入了内容
}
};
return () => (
<container>
<text
text="这是一个按钮"
// 监听 click 事件
onClick={click}
/>
</container>
);
});
```
### 输入数字
`getInput` 类似,不过要用 `getInputNumber` 接口:
```tsx
import { getInputNumber } from '../components';
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(props => {
const click = async () => {
// 调用接口,并等待执行完毕,获取异步返回值
const num = await getInputNumber(
props.controller, // UI 控制器
'请输入一个数字', // 显示的文字
[240, 240, void 0, void 0, 0.5, 0.5], // 输入框位置
240, // 输入框宽度
// 其他参数配置,参考 API 文档
{
// 例如设置一个占位符
input: {
placeholder: '输入数字'
}
}
);
if (isNaN(num)) {
// 如果用户输入的不是数字或点了取消
} else {
// 如果用户输入了数字
}
};
return () => (
<container>
<text
text="这是一个按钮"
// 监听 click 事件
onClick={click}
/>
</container>
);
});
```
## 拓展-API参考
- [ConfirmBox](../../api/user-client-modules/组件%20ConfirmBox.md)

View File

@ -7,13 +7,37 @@
## 客户端内容
以下内容中,一级列表是基础需求指南,二级列表是拓展需求指南。
- [修改状态栏显示](./status-bar.md)
- [可交互按钮](./status-bar.md#拓展-可交互按钮)
- [新增勇士属性](./status-bar.md#拓展-新增勇士属性) (既包含客户端,也包含数据端)
- [编写新 UI](./new-ui.md)
- [自定义按键](./hotkey.md)
- [UI 与组件的区别](./new-ui.md#拓展-ui-与组件的区别)
- [UI 编写参考](../ui.md)
- [新增按键](./hotkey.md)
- [添加辅助按键](./hotkey.md#拓展-添加辅助按键)
- [在 UI 内实现按键](./hotkey.md#拓展-在-ui-内实现按键)
- [单功能多按键](./hotkey.md#拓展-单功能多按键)
- [按下时触发](./hotkey.md#拓展-按下时触发)
- [动画效果](./animate.md)
- [颜色动画](./animate.md#拓展-颜色动画)
- [配合交互](./animate.md#拓展-配合交互)
- [选择框与确认框](./choice.md)
- [使用枚举定义选择框](./choice.md#拓展-使用枚举定义选择框)
- [等待框](./choice.md#拓展-等待框)
- [输入框](./choice.md#拓展-输入框)
## 数据端内容
- [怪物伤害计算](./damage.md)
以下内容中,一级列表是基础需求指南,二级列表是拓展需求指南。
- [怪物特殊属性](./special.md)
- [用函数声明属性](./special.md#拓展-用函数声明属性)
- [地图伤害](./special.md#拓展-地图伤害)
- [光环属性](./special.md#拓展-光环属性)
- [输出回合数](./special.md#拓展-输出回合数)
- [主动技能](./skill.md)
- [多技能设计思路](./skill.md#拓展-多技能设计思路)
- [战后自动关闭技能](./skill.md#拓展-战后自动关闭技能)
- [在开启或关闭技能时执行内容](./skill.md#拓展-在开启或关闭技能时执行内容)

View File

@ -265,7 +265,7 @@ col.applyHalo(
## 拓展-输出回合数
样板默认的 `calDamageWith` 函数只允许输出伤害值,而有时候我们可能会需要战斗的回合数,这时候我们需要修改一下这部分内容,将伤害计算逻辑单独提出来,然后在 `calDamageWith` 中调用它。在需要回合数的时候,我们调用提出了的函数即可,如下例所示:
样板默认的 `calDamageWith` 函数只允许输出伤害值,而有时候我们可能会需要战斗的回合数,这时候我们需要修改一下这部分内容,将伤害计算逻辑单独提出来,命名为 `calDamageWithTurn`然后在 `calDamageWith` 中调用它。在需要回合数的时候,我们调用 `calDamageWithTurn` 函数即可,如下例所示:
```ts
/** 包含回合数的伤害计算 */

View File

@ -181,6 +181,47 @@ leftStatus.def = getHeroStatusOn('def');
leftStatus.atkSpeed = getHeroStatusOn('atkSpeed');
```
状态栏组件中:
```tsx
// 使用模板字符串,显示百分比
<text text={`${s.atkSpeed * 100}%`} />
```
### 属性实现
为了实现勇士属性,我们需要修改伤害计算逻辑。我们打开 `packages-user/data-state/src/enemy/damage.ts`,翻到最后找到 `calDamageWith` 函数,在它上面有一个名为 `realStatus` 的数组,我们在这里新增一项 `atkSpeed`
```ts
/**
* 计算伤害时会用到的勇士属性攻击防御其余的不会有buff加成直接从core.status.hero取
*/
const realStatus: (keyof HeroStatus)[] = [
'atk',
'def',
// ... 原有内容
// 新增 atkSpeed 属性
'atkSpeed' // [!code ++]
];
```
然后在 `calDamageWith` 伤害计算中修改伤害计算,给勇士每回合造成的伤害乘以攻速,注意放置的位置:
```ts
export function calDamageWith(
info: UserEnemyInfo,
hero: Partial<HeroStatus>
): number {
// ... 原有逻辑
// 乘以攻速
heroPerDamage *= hero.atkSpeed ?? 1;
// ... 原有逻辑
}
```
## 拓展-了解 UI 编写的基本逻辑
参考[此文档](./ui.md),此文档将会教你如何从头开始编写一个 UI并解释 UI 运行与渲染的基本逻辑。

View File

@ -6,11 +6,15 @@
参考[此文档](./implements.md)
## 热重载
2.B 样板支持热重载,允许一些内容在不刷新页面的情况下就可以直接更新,包括 UI、怪物数据、道具数据等。例如你在编辑器里面修改了一个怪物的攻击并保存这时候进入游戏界面可以直接看到地图上的怪物伤害变化了怪物手册等 UI 中的怪物数据需要重新打开一次 UI 才会更新)。
## 启动游戏与编辑器
在造塔群中的群文件中找到 `启动服务->2.B+ 启动服务`,根据自己设备的系统下载对应的启动服务(此版本不支持移动端造塔),下载后运行安装到自己的设备,可以选择安装路径。
安装完毕后,打开软件,在左侧点击选择文件夹,然后打开 2.B 样板文件夹,即包含 `package.json` `packages` `packages-user` 这些目录的文件,打开错了会提示打开错误。
安装完毕后,打开软件,在左侧点击选择文件夹,然后打开 2.B 样板文件夹,即包含 `package.json` `packages` `packages-user` 这些目录的文件,打开错了会提示打开错误。
然后点击右侧的安装依赖,耐心等待一段时间,等待依赖安装完毕。
@ -82,7 +86,9 @@ graph TD
### 协议问题
2.B 样板换用了 `GPL3.0` 开源协议,这要求所有以此为基础开发的项目也必须完全开源,但考虑到很多作者不了解其中的细节,因此样板将会针对此问题自动处理,处理方案为:**将源码原封不动地打包为压缩包,放到构建完成的游戏中**,届时,只要在网站上下载游戏,就可以解压压缩包查看源码。
2.B 样板换用了 `GPL3.0` 开源协议,这要求所有以此为基础开发的项目也必须完全开源,但考虑到很多作者不了解其中的细节,因此样板将会针对此问题自动处理,处理方案为:**将源码原封不动地打包为压缩包,放到构建完成的游戏中**,届时,只要在网站上下载游戏,就可以解压压缩包查看源码及开源协议。
同时,这也意味着,如果你使用本样板开发游戏,其他任何人都可以以你的游戏为基础进行二次开发,而不需要你本人同意。如果不想让素材也能被别人使用,可以单独针对素材使用其他协议(如 `CC` 协议,可以上网查询具体信息),而不使用 `GPL3.0` 协议,但代码必须遵循 `GPL3.0` 协议。
## 学会查阅此文档

556
docs/guide/ui/component.md Normal file
View File

@ -0,0 +1,556 @@
# 组件使用指南
2.B 内置了很多实用的组件,本节将会介绍一些常用组件的使用方式。
## 组件引入
组件都在 `packages-user/client-modules/src/render/components` 文件夹中,如果在 `ui` 文件夹下引用,注意路径关系,应该如下引入:
```ts
// 从 components 文件夹引入,注意路径关系
import { TextContent } from '../components';
```
## 组件与元素
一般情况下,组件会包含所有元素拥有的参数,例如 `loc` `alpha` `zIndex`,甚至是 `noevent` `cache` 等,也可以监听 `onClick` `onEnter` 这些交互事件。
除了元素的参数,组件自身可能还会包含一些参数和事件。
有些组件还提供了暴露接口,可以使用这些接口直接控制组件内部。
## 多行文本 TextContent
类似于 2.x 的 `drawTextContent`,可以用于多行文本,包含打字机功能。相比于 `drawTextContent` 的主要优势是支持了英文以及更好的性能表现。
### 显示文字
直接调用组件即可:
```tsx
import { TextContent } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 显示一段长文字
const toShow = 'man what can i say '.repeat(10);
return () => (
<container>
{/* 直接调用组件,其中宽度 width 必填,而 loc 中设定的宽度是无效的 */}
<TextContent loc={[0, 0, 240, 200]} width={240} text={toShow} />
</container>
);
});
```
### 常用配置
| 配置 | 数据类型 | 说明 |
| ------------- | --------- | ------------------------------------------ |
| `font` | `Font` | 文字字体 |
| `interval` | `number` | 打字机效果每两个字之间的时间间隔,单位毫秒 |
| `lineHeight` | `number` | 行间距,单位像素 |
| `fill` | `boolean` | 是否填充文字,默认填充 |
| `stroke` | `boolean` | 是否描边文字,默认不描边 |
| `fillStyle` | `string` | 文字填充样式 |
| `strokeStyle` | `string` | 文字描边样式 |
使用示例:
:::code-group
```tsx [自定义字体]
import { Font } from '@motajs/style'; // [!code ++]
import { TextContent } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 显示一段长文字
const toShow = 'man what can i say '.repeat(10);
// 24px 大小的 Arial 字体
const myFont = new Font('Arial', 24); // [!code ++]
return () => (
<container>
<TextContent
loc={[0, 0, 240, 200]}
width={240}
text={toShow}
font={myFont} // 使用自定义字体 [!code ++]
/>
</container>
);
});
```
```tsx [修改字体颜色]
import { TextContent } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 显示一段长文字
const toShow = 'man what can i say '.repeat(10);
return () => (
<container>
<TextContent
loc={[0, 0, 240, 200]}
width={240}
text={toShow}
fill // 文字填充 [!code ++]
stroke // 文字描边 [!code ++]
fillStyle="yellow" // 文字填充为黄色 [!code ++]
strokeStyle="cyan" // 文字描边为青色 [!code ++]
/>
</container>
);
});
```
```tsx [修改行间距]
import { TextContent } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 显示一段长文字
const toShow = 'man what can i say '.repeat(10);
return () => (
<container>
<TextContent
loc={[0, 0, 240, 200]}
width={240}
text={toShow}
lineHeight={8} // 行间距为 8px [!code ++]
/>
</container>
);
});
```
:::
### 自动调整高度
如果我们不知道这些文字显示的时候会有多高,那么我们可以使用 `autoHeight` 来让组件自动确定自身的高度,可以用于滚动条等场景。
```tsx
import { TextContent } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 显示一段长文字
const toShow = 'man what can i say '.repeat(10);
return () => (
<container>
<TextContent
loc={[0, 0, 240, 200]}
width={240}
text={toShow}
autoHeight // 使用 autoHeight让组件自动确定自己的高度 [!code ++]
/>
</container>
);
});
```
### 转义字符
与 2.x 类似2.B 的 `TextContent` 也允许转义字符。具体请参考[此文档](../../api/user-client-modules/TextContentParser.md#转义字符语法说明)。
### API 参考
`TextContent` 还有很多功能和特性,这里只做最基础的教学,如果需要其他功能,请参考[API 文档](../../api/user-client-modules/组件%20TextContent.md)
## 滚动条 Scroll
`Scroll` 是滚动条组件,如果内容在一个界面大小内显示不下,可以使用滚动条组件,这样玩家可以使用鼠标滚轮或点击拖动来滚动内容。
### 基本使用
直接调用组件,然后填写组件内容即可:
```tsx {7-10}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
// loc 必填,表示组件位置
<Scroll loc={[0, 0, 416, 416]}>
<text text="text1" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
### 平铺布局
在使用 `Scroll` 组件时,我们推荐使用平铺式布局,即所有内容平铺在 `Scroll` 组件中,而不是将整体用一个 `container` 将它包起来,这非常有助于提高滚动条组件的性能。
例如下面这种格式就是**好的写法**
```tsx {8-10}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
<Scroll loc={[0, 0, 416, 416]}>
{/* 平铺内容,性能表现更好 */}
<text text="text1" />
<text text="text2" />
<text text="text3" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
而下面这种就是**不好的写法**
```tsx {8-12}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
<Scroll loc={[0, 0, 416, 416]}>
{/* 用一个 container 包裹,性能表现不好 */}
<container>
<text text="text1" />
<text text="text2" />
<text text="text3" />
</container>
</Scroll>
);
});
```
### 横向滚动条
添加 `hor` 标记,即可使滚动条变为横向。暂时没有既可以横向又可以纵向的滚动条。
```tsx {7}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
// 添加 hor 标记,变为横向
<Scroll loc={[0, 0, 416, 416]} hor>
<text text="text1" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
### 隐藏滚动条
有时候可能需要隐藏滚动条(例如样板的浏览地图界面中左侧的楼层列表),可以使用 `noscroll` 标记实现:
```tsx {7}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
// 添加 noscroll 标记,隐藏滚动条
<Scroll loc={[0, 0, 416, 416]} noscroll>
<text text="text1" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
### 布局补偿
有时候我们需要在滚动条滚动到最后时填充一些空白内容,可以使用 `padEnd` 来实现:
```tsx {7}
import { Scroll } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
// 使用 padEnd 在滚动条最后填充空白内容,单位像素
<Scroll loc={[0, 0, 416, 416]} padEnd={120}>
<text text="text1" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
### 代码控制滚动条
可以使用 `Scroll` 提供的接口来用代码控制滚动条:
```tsx {5-12,15-16}
import { Scroll, ScrollExpose } from '../components'; // [!code ++]
import { vue } from 'vue'; // [!code ++]
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 使用响应式变量定义 scroll 引用
const scrollRef = ref<ScrollExpose>();
onMounted(() => {
// 滚动至 100 像素的位置,动画时长 500ms
scrollRef.value?.scrollTo(100, 500);
});
return () => (
// 将 ref 属性设为 scrollRef 来获取 Scroll 的接口
<Scroll loc={[0, 0, 416, 416]} ref={scrollRef}>
<text text="text1" />
{/* 省略更多内容 */}
</Scroll>
);
});
```
### API 参考
API 参考[此文档](../../api/user-client-modules/组件%20Scroll.md)。
## 图标组件
`components/icons.tsx` 中内置了一些图标,可以直接使用。
### 基本使用
以箭头图标为例:
```tsx {8}
import { ArrowDownTailless } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
<container>
{/* 调用图标组件 */}
<ArrowDownTailless loc={[0, 0, 48, 48]} />
</container>
);
});
```
所有图标都可以填写图形元素的参数,例如 `fill` `stroke` `strokeStyle` `lineWidth` 等。大部分图标默认都是描边样式,如果使用填充可能会导致效果不好。例如修改描边样式和线宽:
```tsx {8}
import { ArrowDownTailless } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
<container>
<ArrowDownTailless
loc={[0, 0, 48, 48]}
strokeStyle="cyan" // 描边使用青色
lineWidth={2} // 线宽为 2px
/>
</container>
);
});
```
### 图标列表
目前样板包含这些图标:
- `RollbackIcon`: 回退图标
- `RetweenIcon`: 回收图标
- `ViewMapIcon`: 浏览地图图标
- `DanmakuIcon`: 弹幕图标
- `ReplayIcon`: 回放图标
- `numpadIcon`: 数字键盘图标
- `PlayIcon`: 开始播放图标
- `PauseIcon`: 暂停播放图标
- `DoubleArrow`: 双箭头图标(向右)
- `StepForward`: 单步向前图标
- `SoundVolume`: 音量图标
- `Fullscreen`: 全屏图标
- `ExitFullscreen`: 退出全屏图标
- `ArrowLeftTailless`: 无尾巴左箭头图标
- `ArrowRightTailless`: 无尾巴右箭头图标
- `ArrowUpTailless`: 无尾巴上箭头图标
- `ArrowDownTailless`: 无尾巴下箭头图标
### API 及参数参考
参数参考[此文档](../../api/motajs-render-vue/GraphicBaseProps.md)。
API 参考[此文档](../../api/user-client-modules/图标组件.md)
## 分页 Page
分页可以用来展示大量内容,在极端情况下,其性能要比 `Scroll` 更好,但交互手感与易用性不如滚动条。
### 基本使用
`Page` 组件需要传入两个必填参数 `pages``loc``pages` 代表总页数,`loc` 代表位置。
每个页面的内容使用插槽形式传入,接收 `page` 作为参数,代表当前是第几页。你可能不理解这句话,用代码写的话就是这样:
```tsx {6-17}
import { Page } from '../components';
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
return () => (
<Page
loc={[0, 0, 416, 416]} // 位置
pages={10} // 总页码数
>
{/* 插槽内容,传入一个函数,参数代表当前是第几页 */}
{(page: number) => (
<container>
{/* 这里面填写当前页显示的内容,例如添加一个文字显示当前第几页 */}
<text text={page.toString()} />
</container>
)}
</Page>
);
});
```
### 第一种设置与获取当前页
可以通过 `v-model` 指令来创建双向数据绑定,从而达到设置与获取当前页码的功能。
这种方式一般情况下相对来说没有下一节提到的方式更好,因此相对不推荐。使用示例如下:
```tsx {7-13}
import { Page } from '../components';
// 从 vue 引入 ref 响应式函数
import { ref, watch } from 'vue'; // [!code ++]
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 定义响应式变量
const nowPage = ref(0);
// 监听当且页码的变化,需要的时候直接用 nowPage.value 获取也可
watch(nowPage, value => core.drawTip(`切换至${value}页!`));
/** 设置当前页码 */
const changePage = (value: number) => void (nowPage.value = value);
return () => (
<Page
loc={[0, 0, 416, 416]}
pages={10}
// 使用 v-model 指令创建双向数据绑定
v-model:page={nowPage.value} // [!code ++]
>
{(page: number) => (
<container>
<text text={page.toString()} />
</container>
)}
</Page>
);
});
```
### 第二种设置与获取当前页方式
相比于上一种方式,我们更推荐使用下面这种方式来设置与获取当前页。
可以使用 `Page` 组件提供的 `changePage``movePage` 来切换页码,其中前者是直接切换至某一页,后者是在当前页的基础上移动页码数。
可以使用 `now` 来获取当前页。
二者的示例如下:
:::code-group
```tsx [切换页码] {6-16,19-20}
import { Page, PageExpose } from '../components'; // [!code ++]
import { vue } from 'vue'; // [!code ++]
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 使用响应式变量定义 Page 引用
const pageRef = ref<PageExpose>();
onMounted(() => {
// 切换至第五页
pageRef.value?.changePage(5);
// 在当前页的基础上增加两页,也就是切换到第七页
pageRef.value?.movePage(2);
// 在当前页的基础上减少两页,也就是切换回第五页
pageRef.value?.movePage(-2);
});
return () => (
// 将 ref 属性赋值为 pageRef 来获取其接口
<Page loc={[0, 0, 416, 416]} pages={10} ref={pageRef}>
{(page: number) => (
<container>
<text text={page.toString()} />
</container>
)}
</Page>
);
});
```
```tsx [获取页码] {6-12,15-16}
import { Page, PageExpose } from '../components'; // [!code ++]
import { vue } from 'vue'; // [!code ++]
// 不再展示完整 UI 模板,只展示核心部分
export const MyCom = defineComponent(() => {
// 使用响应式变量定义 Page 引用
const pageRef = ref<PageExpose>();
onMounted(() => {
const page = pageRef.value?.now();
core.drawTip(`当前是第${page}页!`);
});
return () => (
// 将 ref 属性赋值为 pageRef 来获取其接口
<Page loc={[0, 0, 416, 416]} pages={10} ref={pageRef}>
{(page: number) => (
<container>
<text text={page.toString()} />
</container>
)}
</Page>
);
});
```
:::
## 更多组件
本文只简单讲解了部分常用的组件,样板还有很多内置的组件。以下是所有内置组件的 API 参考:
- [ConfirmBox](../../api/user-client-modules/组件%20ConfirmBox.md):确认框,一般使用 `getConfirm` 接口,不直接使用组件。
- [Choices](../../api/user-client-modules/组件%20Choices.md):选择框,一般使用 `getChoice` 接口,不直接使用组件。
- [FloorSelector](../../api/user-client-modules/组件%20FloorSelector.md):楼层选择组件,浏览地图左侧的楼层选择就是使用的本组件。
- [图标组件](../../api/user-client-modules/图标组件.md):一些常用图标。
- [Input](../../api/user-client-modules/组件%20Input.md):输入组件,可以放到组件内部,可以用于搜索栏等。
- [InputBox](../../api/user-client-modules/组件%20InputBox.md):输入框组件,类似于确认框,一般使用 `getInput``getInputNumber` 接口,不使用本组件。
- [List](../../api/user-client-modules/组件%20List.md):列表组件,可以用于展示一列内容。
- [ListPage](../../api/user-client-modules/组件%20ListPage.md):左侧是列表,右侧是当前选项对应的详情页,可以用于游戏机制说明等。
- [Progress](../../api/user-client-modules/组件%20Progress.md):进度条组件,播放录像时右下角的进度条就是本组件。
- [Arrow](../../api/user-client-modules/组件%20Arrow.md):箭头组件,画一个箭头。
- [ScrollText](../../api/user-client-modules/组件%20ScrollText.md):滚动文本组件,可以用于长剧情或是 staff 表等。
- [Selection](../../api/user-client-modules/组件%20Selection.md):选择光标,列表组件的选择光标就是使用的本组件。
- [Background](../../api/user-client-modules/组件%20Background.md):背景组件,可以设置为纯色或 `winskin`
- [WaitBox](../../api/user-client-modules/组件%20WaitBox.md):等待框,一般使用 `waitbox` 接口,不直接使用组件。
- [Page](../../api/user-client-modules/组件%20Page.md):分页组件,本文已经详细讲解。
- [Scroll](../../api/user-client-modules/组件%20Scroll.md):滚动条组件,本文已经详细讲解。
- [TextContent](../../api/user-client-modules/组件%20TextContent.md):多行文本组件,本文已经详细讲解。
- [Textbox](../../api/user-client-modules/组件%20Textbox.md):文本框组件,就是事件的显示文字,一般不会直接用。
- [Thumbnail](../../api/user-client-modules/组件%20Thumbnail.md):缩略图组件,用于展示某个地图的缩略图。
- [Tip](../../api/user-client-modules/组件%20Tip.md):提示组件,就是左上角的提示,一般不会直接使用。

View File

@ -349,7 +349,7 @@ type RenderFn = (canvas: MotaOffscreenCanvas2D, transform: Transform) => void;
- `canvas`: 要渲染至的画布,一般直接将内容渲染至这个画布上
- `transform`: 当前元素的变换矩阵,相对于父元素,不常用
多数情况下,我们只会使用到第一个参数,`MotaOffscreenCanvas2D` 接口请参考 [API 文档](../api/motajs-render-core/index.md)。下面是一个典型案例:
多数情况下,我们只会使用到第一个参数,`MotaOffscreenCanvas2D` 接口请参考 [API 文档](../../api/motajs-render-core/index.md)。下面是一个典型案例:
```tsx
const render = (canvas: MotaOffscreenCanvas2D) => {
@ -753,4 +753,4 @@ const themeStyle = {
## API 参考
参考[API 文档](../api/motajs-render-vue/index.md),这里有更细致的 API 介绍。
参考[API 文档](../../api/motajs-render-vue/index.md),这里有更细致的 API 介绍。

View File

@ -31,7 +31,7 @@ watch(data, () => mySprite.value?.update());
## 我的 UI 很卡
可能使用了平铺式布局,建议使用 `Scroll` 组件或者 `Page` 组件来对平铺内容分割,从而提高渲染效率。可以参考对应的 [API 文档](../api/user-client-modules/组件%20Scroll.md)。
可能使用了平铺式布局,建议使用 `Scroll` 组件或者 `Page` 组件来对平铺内容分割,从而提高渲染效率。可以参考对应的 [API 文档](../../api/user-client-modules/组件%20Scroll.md)。
## 玩着玩着突然黑屏了一下,然后画面就不显示了

17
docs/guide/ui/index.md Normal file
View File

@ -0,0 +1,17 @@
# UI 系统指南
2.B 提供了强大的 UI 系统,可以让你更快地做出更好看的 UI。
## 热重载
2.B 对 UI 有热重载支持,当你修改了代码时,你可以不刷新游戏画面就看到 UI 的变化,可以省去大量时间。
## 指南目录
- [UI 编写](./ui.md):教你如何从头开始编写一个 UI包括界面显示、交互等。
- [UI 元素](./elements.md):讲述所有常用的 UI 元素(标签),包括图片、图形、文字等。
- [组件使用指南](./component.md):教你如何使用一些常用组件。
- [优化性能](./perf.md):教你如何优化一个 UI 的性能表现。
- [UI 系统](./system.md):解释 UI 系统中 UI 控制器的工作原理,重点在于 UI 的打开与关闭。
- [常见问题](./faq.md):编写 UI 时的常见问题解答。
- [未来规划](./future.md)UI 系统的未来规划。

View File

@ -45,7 +45,7 @@ lang: zh-CN
## 使用 `Scroll``Page` 组件优化平铺性能
在一些特殊情况下,我们不得不使用平铺布局,例如上一节提到的怪物手册,或是展示一个列表等,这时候必须平铺元素。这时候我们可以使用 `Scroll` 组件或 `Page` 组件来优化性能表现。`Scroll` 组件中,只有在画面内的元素会被渲染,而画面外的不会被渲染,这会大大提高渲染效率;`Page` 组件允许你把列表拆分成多个部分,然后把内容放在不同页中,从而提高渲染性能。极端情况下,`Page` 组件的渲染效率要明显高于 `Scroll` 组件,但是滚动条对于交互更友好,我们推荐在简单场景下使用 `Scroll` 组件,而对于复杂场景,换为 `Page` 组件。两个组件的使用方式可以参考 [API 文档](../api/motajs-render-elements/)。
在一些特殊情况下,我们不得不使用平铺布局,例如上一节提到的怪物手册,或是展示一个列表等,这时候必须平铺元素。这时候我们可以使用 `Scroll` 组件或 `Page` 组件来优化性能表现。`Scroll` 组件中,只有在画面内的元素会被渲染,而画面外的不会被渲染,这会大大提高渲染效率;`Page` 组件允许你把列表拆分成多个部分,然后把内容放在不同页中,从而提高渲染性能。极端情况下,`Page` 组件的渲染效率要明显高于 `Scroll` 组件,但是滚动条对于交互更友好,我们推荐在简单场景下使用 `Scroll` 组件,而对于复杂场景,换为 `Page` 组件。两个组件的使用方式可以参考 [API 文档](../../api/motajs-render-elements/)。
我们建议:

View File

@ -3,7 +3,52 @@
本节将会讲解 2.B 的渲染树与 UI 系统的工作原理,以及一些常用 API。
:::info
**这部分可以选择性阅读,多数功能一般场景下用不到。**
**这部分可以选择性阅读,只有打开和关闭较为重要,其他功能一般场景下用不到。**
:::
## 打开与关闭 UI
在 UI 编写章节已经提到了打开和关闭 UI 使用 `open``close` 方法,现在我们更细致地讲解一下如何打开与关闭 UI。打开 UI 使用 `open` 方法,定义如下:
```ts
function open<T extends UIComponent>(
ui: IGameUI<T>,
props: UIProps<T>,
alwaysShow?: boolean
): IUIInstance;
```
其中第一个参数表示要打开的 UI第二个表示传给 UI 的参数,第三个表示 UI 是否永远保持显示状态(除非被关闭),不受到显示模式的影响。同种 UI 可以打开多个,也可以在不同的控制器上同时打开多个相同的 UI。例如如果我们想在主 UI 控制器中添加一个常量的返回游戏按钮,就可以这么写:
```ts
// BackToGame 是自定义 UI第三个参数传 true 来保证它一直显示在画面上
mainUIController.open(BackToGame, {}, true);
```
关闭 UI 使用 `close` 方法,传入 UI 实例,即 `open` 方法的返回值,没有其他参数。例如:
```ts
const MyUI = defineComponent(props => {
// 所有通过 UI 控制器打开的,同时按照 UI 模板填写了 props 的 UI 都包含 controller 和 instance 属性
props.controller.close(props.instance);
}, myUIProps);
```
除此之外,还提供了一个关闭所有 UI 的:
```ts
function closeAll(ui?: IGameUI): void;
```
其中参数表示要关闭的 UI 类型,不填时表示关闭所有 UI填写时表示关闭所有指定类型的 UI。例如我想关闭所有 `EnemyInfo` UI可以这么写
```ts
// EnemyInfo 是自定义 UI
mainUIController.closeAll(EnemyInfo);
```
:::warning
以下内容属于进阶内容,没有高级需求不需要理解。
:::
## 创建一个自己的 UI 管理器
@ -149,47 +194,6 @@ keep.safelyUnload();
keep.unload();
```
## 打开与关闭 UI
在 UI 编写章节已经提到了打开和关闭 UI 使用 `open``close` 方法,现在我们更细致地讲解一下如何打开与关闭 UI。打开 UI 使用 `open` 方法,定义如下:
```ts
function open<T extends UIComponent>(
ui: IGameUI<T>,
props: UIProps<T>,
alwaysShow?: boolean
): IUIInstance;
```
其中第一个参数表示要打开的 UI第二个表示传给 UI 的参数,第三个表示 UI 是否永远保持显示状态(除非被关闭),不受到显示模式的影响。同种 UI 可以打开多个,也可以在不同的控制器上同时打开多个相同的 UI。例如如果我们想在主 UI 控制器中添加一个常量的返回游戏按钮,就可以这么写:
```ts
// BackToGame 是自定义 UI第三个参数传 true 来保证它一直显示在画面上
myController.open(BackToGame, {}, true);
```
关闭 UI 使用 `close` 方法,传入 UI 实例,即 `open` 方法的返回值,没有其他参数。例如:
```ts
const MyUI = defineComponent(props => {
// 所有通过 UI 控制器打开的,同时按照 UI 模板填写了 props 的 UI 都包含 controller 和 instance 属性
props.controller.close(props.instance);
}, myUIProps);
```
除此之外,还提供了一个关闭所有 UI 的:
```ts
function closeAll(ui?: IGameUI): void;
```
其中参数表示要关闭的 UI 类型,不填时表示关闭所有 UI填写时表示关闭所有指定类型的 UI。例如我想关闭所有 `EnemyInfo` UI可以这么写
```ts
// EnemyInfo 是自定义 UI
myController.closeAll(EnemyInfo);
```
## 渲染系统的树结构
接下来我们来讲解一下渲染系统的一些工作原理。下面的部分由 `DeepSeek R1` 模型生成并稍作修改。

View File

@ -58,7 +58,7 @@ return () => (
## 显示 UI
我们编写完 UI 之后,这个 UI 并不会自己显示,需要手动打开。我们找到 `ui/main.tsx`,在 `MainScene` 这个根组件中添加一句话
我们编写完 UI 之后,这个 UI 并不会自己显示,需要手动打开。我们找到 `ui/main.tsx`,在 `MainScene` 这个根组件中调用 `mainUIController.open`
```ts
// 在这添加引入
@ -68,7 +68,7 @@ const MainScene = defineComponent(() => {
// ... 其他内容
// 在这添加一句话,打开 UI第二个参数为传入 UI 的参数,后面会有讲解
// 纵深设为 100 以保证可以显示出来,纵深越大,元素越靠上,会覆盖纵深低的元素
mainUIController.open(MyBookUI, { zIndex: 100 });
mainUIController.open(MyBookUI, { zIndex: 100 }); // [!code ++]
return () => (
// ... 其他内容
);
@ -80,7 +80,7 @@ const MainScene = defineComponent(() => {
```tsx
export const MyBook = defineComponent<MyBookProps>(props => {
// 例如,我们可以让它在打开 10 秒钟后关闭:
setTimeout(() => props.controller.close(props.instance), 10000);
setTimeout(() => props.controller.close(props.instance), 10000); // [!code ++]
return () => (
// ... UI 内容
);
@ -119,7 +119,9 @@ import { UIController } from '@motajs/system-ui';
const mainUIController = UIController.getController('main-ui');
```
更多的 UI 控制功能可以参考后续文档以及相关的 [UI 系统指南](./ui-system.md) 或 [API 文档](../api/motajs-system-ui/UIController)。
关于 UI 打开与关闭的细节参考[此文档](./system.md#打开与关闭-ui)
更多的 UI 控制功能可以参考后续文档以及相关的 [UI 系统指南](./system.md) 或 [API 文档](../../api/motajs-system-ui/UIController)。
## 添加更多内容
@ -169,7 +171,7 @@ return () => (
);
```
更多的字体使用方法可以参考 [API 文档](../api/motajs-render-style/Font)
更多的字体使用方法可以参考 [API 文档](../../api/motajs-render-style/Font)
### 圆角矩形
@ -820,7 +822,7 @@ watch(selected, () => {
## 修改 UI 参数
在打开 UI 时,我们可以传入参数,默认情况下,可以传入所有的 `BaseProps`,也就是所有元素通用属性,以及自己定义的 UI 参数。`BaseProps` 内容较多,可以参考 [API 文档](../api/motajs-render-core/RenderItem.md)。除此之外,我们还为这个自定义怪物手册添加了 `floorId` 参数,它也可以在打开 UI 时传入。如果需要打开的 UI 参数具有响应式,例如可以动态修改楼层 id可以使用 `reactive` 方法。示例如下:
在打开 UI 时,我们可以传入参数,默认情况下,可以传入所有的 `BaseProps`,也就是所有元素通用属性,以及自己定义的 UI 参数。`BaseProps` 内容较多,可以参考 [API 文档](../../api/motajs-render-core/RenderItem.md)。除此之外,我们还为这个自定义怪物手册添加了 `floorId` 参数,它也可以在打开 UI 时传入。如果需要打开的 UI 参数具有响应式,例如可以动态修改楼层 id可以使用 `reactive` 方法。示例如下:
```ts
import { MyBookProps, MyBookUI } from './myUI';

View File

@ -1,4 +1,5 @@
export * from './choices';
export * from './floorSelect';
export * from './icons';
export * from './input';
export * from './list';

View File

@ -517,4 +517,12 @@ export async function getInputNumber(
return parseFloat(value);
}
export async function routedInput() {
// todo
}
export async function routedInputNumber() {
// todo
}
export const InputBoxUI = new GameUI('input-box', InputBox);

View File

@ -230,12 +230,13 @@ export const Page = defineComponent<
<container loc={contentLoc.value}>
{slots.default?.(nowPage.value)}
</container>
<container loc={pageLoc.value} hidden={hide.value}>
<container loc={pageLoc.value} hidden={hide.value} nocache>
<container
key={1}
loc={leftLoc.value}
onClick={lastPage}
cursor="pointer"
nocache
>
<g-rectr
loc={rectLoc.value}
@ -257,6 +258,7 @@ export const Page = defineComponent<
loc={leftPageLoc.value}
onClick={lastPage}
cursor="pointer"
nocache
>
<g-rectr
loc={rectLoc.value}
@ -272,7 +274,7 @@ export const Page = defineComponent<
></text>
</container>
)}
<container loc={nowPageLoc.value} key={3}>
<container loc={nowPageLoc.value} key={3} nocache>
<g-rectr
loc={rectLoc.value}
circle={[round.value]}
@ -295,6 +297,7 @@ export const Page = defineComponent<
loc={rightPageLoc.value}
onClick={nextPage}
cursor="pointer"
nocache
>
<g-rectr
loc={rectLoc.value}
@ -315,6 +318,7 @@ export const Page = defineComponent<
loc={rightLoc.value}
onClick={nextPage}
cursor="pointer"
nocache
>
<g-rectr
loc={rectLoc.value}