docs: 动画指南

This commit is contained in:
unanmed 2025-08-22 18:14:18 +08:00
parent a10af04b79
commit c26ae39060
7 changed files with 204 additions and 11 deletions

View File

@ -72,8 +72,12 @@ export default defineConfig({
link: '/guide/implements/skill'
},
{
text: '自定义按键',
text: '新增按键',
link: '/guide/implements/hotkey'
},
{
text: '动画效果',
link: '/guide/implements/animate'
}
]
}

View File

@ -0,0 +1,182 @@
# 动画效果
2.B 提供了专门的动画接口,允许你用短短几行就可以做出一个效果不错的动画。本节主要讲述的是 UI 中的动画。
## 定义动画属性
我们以一个自定义 UI 为例,来讲述如何编写一段动画。自定义 UI 参考[此指南](./new-ui.md)和[此教程](../../guide/ui.md)。
假设自定义 UI 在 `packages-user/client-modules/src/render/ui` 文件夹下,我们需要引入 `transitioned` 接口,并调用它定义动画属性:
```tsx {8-13}
// 引入接口,如果文件夹不同,注意路径关系
import { transitioned } from '../use';
// 从 mutate-animate 库中引入速率曲线函数
import { hyper } from 'mutate-animate';
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(() => {
// 定义动画属性
const value = transitioned(
1, // 初始值是 1
500, // 动画时长 500ms
hyper('sin', 'out') // 速率曲线是双曲正弦函数(此曲线观感较好)
)!;
// 上一行的感叹号为非空断言,因为此接口在组件或 UI 外调用会返回 null
// 而我们在 UI 内,因此我们需要断言其不可能为 null
return () => <container></container>;
});
```
注意 `transitioned` 只能在 UI 或组件的顶层调用,不能在 UI 的函数内调用,也不可在 UI 外调用,在其他地方调用会返回 `null`
## 添加到元素属性
定义属性后,我们需要把属性添加到元素上,直接使用 `value.ref.value` 即可,这里以不透明度 `alpha` 属性为例:
```tsx {9}
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(() => {
// 定义动画属性
const value = transitioned(1, 500, hyper('sin', 'out'))!;
return () => (
<container>
{/* 将 value.ref.value 赋值给 alpha 属性 */}
<g-rect rect={[0, 0, 100, 100]} alpha={value.ref.value} />
</container>
);
});
```
## 执行动画
最后,在我们需要的时候执行动画即可,使用 `value.set`
```tsx
export const MyCom = defineComponent(() => {
// 定义动画属性
const value = transitioned(1, 500, hyper('sin', 'out'))!;
// 例如在 UI 挂载完毕后执行
onMounted(() => {
// 动画属性调成 0效果就是矩形逐渐从不透明变成完全透明
value.set(0);
});
return () => (
<container>
<g-rect rect={[0, 0, 100, 100]} alpha={value.ref.value} />
</container>
);
});
```
## 拓展-颜色动画
除了一般数值的动画外,样板还支持颜色动画,使用 `transitionedColor` 接口,用法与 `transitioned` 基本一致:
```tsx {23}
// 引入接口,如果文件夹不同,注意路径关系
import { transitionedColor } from '../use'; // [!code ++]
// 从 mutate-animate 库中引入速率曲线函数
import { hyper } from 'mutate-animate';
export const MyCom = defineComponent(() => {
// 定义颜色动画
const color = transitionedColor(
'#fff', // 初始颜色,设定为白色 // [!code ++]
1000, // 动画时长 1s
hyper('sin', 'out') // 动画速率曲线
)!;
// 例如在 UI 挂载完毕后执行
onMounted(() => {
// 颜色调成纯黑
color.set('#000'); // [!code ++]
});
return () => (
<container>
{/* 将 fillStyle 赋值为 color.ref.value */}
<g-rect rect={[0, 0, 100, 100]} fillStyle={color.ref.value} />
</container>
);
});
```
颜色目前仅支持输入 `#RGB` `#RGBA` `#RRGGBB` `#RRGGBBAA` `rgb(r,g,b)` `rgba(r,g,b,a)` 几种格式的字符串。
## 拓展-配合交互
我们可以配合交互实现以下几种功能:
### 鼠标移入移出
当鼠标移入时高亮,当移出时恢复:
```tsx {8-11,19-20}
import { transitionedColor } from '../use';
import { hyper } from 'mutate-animate';
export const MyCom = defineComponent(() => {
// 初始颜色为灰色
const color = transitionedColor('#aaa', 200, hyper('sin', 'out'))!;
/** 鼠标移入时变成白色 */
const enter = () => color.set('#fff');
/** 鼠标移出时恢复为灰色 */
const leave = () => color.set('#aaa');
return () => (
<container>
<g-rect
rect={[0, 0, 100, 100]}
fillStyle={color.ref.value}
// 监听 enter 和 leave 事件
onEnter={enter}
onLeave={leave}
/>
</container>
);
});
```
### 点击切换状态
鼠标点击或触碰点击后高亮,再次点击恢复:
```tsx {8-15,23}
import { transitionedColor } from '../use';
import { hyper } from 'mutate-animate';
export const MyCom = defineComponent(() => {
// 初始颜色为灰色
const color = transitionedColor('#aaa', 200, hyper('sin', 'out'))!;
let highlight = false;
/** 点击时触发 */
const click = () => {
// 如果已经高亮,那么恢复为灰色,否则高亮为白色
color.set(highlight ? '#aaa' : '#fff');
// 切换状态
highlight = !highlight;
};
return () => (
<container>
<g-rect
rect={[0, 0, 100, 100]}
fillStyle={color.ref.value}
// 监听 click 事件
onClick={click}
/>
</container>
);
});
```
## 拓展-API 参考
参考[此 API 文档](../../api/user-client-modules/functions.md#transitioned)。

View File

@ -1,4 +1,4 @@
# 自定义按键
# 新增按键
在 2.B 中新增按键很方便,且可以为你的 UI 单独配置按键信息,玩家也可以修改快捷键设置。
@ -91,8 +91,8 @@ gameKey
```tsx {7-13}
// 引入 useKey 接口
// 文件在 packages-user/client-modules/src/render/utils/use.ts注意路径关系
import { useKey } from '../utils/use'; // [!code ++]
// 文件在 packages-user/client-modules/src/render/use.ts注意路径关系
import { useKey } from '../use'; // [!code ++]
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(props => {
@ -113,7 +113,7 @@ export const MyCom = defineComponent(props => {
我们会有一些通用按键,例如确认、关闭,这些按键我们不希望每个 UI 或场景都定义一遍,一来写代码不方便,二来玩家如果要自定义的话需要每个界面都设置一遍,很麻烦。此时我们建议按键复用。与一般的按键一致,我们直接实现 `exit` `confirm` 等按键即可,不需额外操作:
```tsx {12-21}
import { useKey } from '../utils/use';
import { useKey } from '../use';
// UI 模板及如何编写 UI 参考 “新增 UI” 需求指南,这里只给出必要的修改部分,模板部分不再给出
export const MyCom = defineComponent(props => {

View File

@ -750,3 +750,7 @@ const themeStyle = {
<g-rect fill {...themeStyle} />
<g-circle stroke {...themeStyle} />
```
## API 参考
参考[API 文档](../api/motajs-render-vue/index.md),这里有更细致的 API 介绍。

View File

@ -2,12 +2,18 @@
本节将会讲解 2.B 的渲染树与 UI 系统的工作原理,以及一些常用 API。
:::info
**这部分可以选择性阅读,多数功能一般场景下用不到。**
:::
## 创建一个自己的 UI 管理器
样板提供 `UIController` 类,允许你在自己的一个 UI 中创建自己的 UI 管理器,例如在样板中,游戏画面本身包含一个 UI 管理器,分为了封面、加载界面、游戏界面三种,其中游戏界面里面还有一个游戏 UI 管理器,我们常用的就是最后一个游戏 UI 管理器。
多数情况下,样板自带的 UI 管理器已经足够,不需要自己创建 UI 管理器。我们最常用的管理器就是 `mainUIController`,它控制了游戏界面下的 UI。
这里为了教程的完整性,简单讲述一下如何创建一个自己的 UI 管理器。
### 创建 UIController 实例
我们从 `@motajs/system-ui` 引入 `UIController` 类,然后对其实例化:

View File

@ -8,7 +8,7 @@ lang: zh-CN
## 创建 UI 文件
首先,我们打开 `packages-user/client-modules/render` 文件夹,这里是目前样板的 UI 目录(之后可能会修改),我们可以看到 `components` `legacy` `ui` 三个文件夹,其中 `component` 是组件文件夹,也就是所有 UI 都可能用到的组件,例如滚动条、分页、图标等,这些东西不会单独组成一个 UI但是可以方便 UI 开发。`legacy` 文件夹是将要删除或重构的内容,不建议使用里面的内容。`ui` 就是 UI 文件夹,这里面存放了所有的 UI我们在这里创建一个文件 `myUI.tsx`
首先,我们打开 `packages-user/client-modules/render` 文件夹,这里是目前样板的 UI 目录(之后可能会修改),我们可以看到 `components` `ui` 等文件夹,其中 `component` 是组件文件夹,也就是所有 UI 都可能用到的组件,例如滚动条、分页、图标等,这些东西不会单独组成一个 UI但是可以方便 UI 开发。`ui` 就是 UI 文件夹,这里面存放了所有的 UI我们在此文件夹下创建一个文件 `myUI.tsx`
## 编写 UI 模板
@ -87,7 +87,7 @@ export const MyBook = defineComponent<MyBookProps>(props => {
}, myBookProps);
```
除此之外,我们还可以在任意渲染端模块中引入 `ui/controller` 来获取到根组件的 UI 控制器,注意跨文件夹引入时需要引入 `@user/client-modules`。例如,我们可以在其他文件中控制这个 UI 的开启与关闭:
除此之外,我们还可以在任意渲染端模块中引入 `ui/controller` 来获取到根组件的 UI 控制器。例如,我们可以在其他文件中控制这个 UI 的开启与关闭:
```ts
import { mainUIController, MyBookUI } from './ui';
@ -132,7 +132,7 @@ const mainUIController = UIController.getController('main-ui');
```tsx
return () => (
<container>
{/* 显示绿史莱姆图标,位置在 (32, 32),循环播放动画 */}
{/* 显示绿史莱姆图标,位置在 (32, 32)animate 表示循环播放动画 */}
<icon icon="greenSlime" loc={[32, 32]} animate />
</container>
);

View File

@ -5,10 +5,7 @@
"type": "module",
"scripts": {
"dev": "tsx script/dev.ts",
"build": "vue-tsc && vite build && tsx script/build.ts dist",
"build-local": "vue-tsc && vite build && tsx script/build.ts local",
"preview": "vite preview",
"update": "tsx script/update.ts",
"declare": "tsx script/declare.ts",
"type": "vue-tsc --noEmit",
"lines": "tsx script/lines.ts packages packages-user",