mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-19 17:16:08 +08:00
docs: @user/client-modules & fix: 修复 client-modules 中的一些错误
This commit is contained in:
parent
1d62a404b7
commit
0a2cdbee79
@ -25,7 +25,7 @@
|
||||
## 构造方法
|
||||
|
||||
```typescript
|
||||
function constructor(patchClass: T): T;
|
||||
function constructor<T extends PatchClass>(patchClass: T): Patch<T>;
|
||||
```
|
||||
|
||||
- **参数**
|
||||
|
142
docs/api/user-client-modules/AudioDecoder.md
Normal file
142
docs/api/user-client-modules/AudioDecoder.md
Normal file
@ -0,0 +1,142 @@
|
||||
# AudioDecoder API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音频解码系统的核心抽象类,为不同音频格式提供统一的解码接口。主要处理浏览器原生不支持音频格式的解码任务(如 iOS 平台的 Ogg 格式)。
|
||||
|
||||
---
|
||||
|
||||
## 静态成员说明
|
||||
|
||||
### `decoderMap`
|
||||
|
||||
```typescript
|
||||
declare const decoderMap: Map<AudioType, new () => AudioDecoder>;
|
||||
```
|
||||
|
||||
解码器注册表,存储格式类型与解码器类的映射关系
|
||||
|
||||
---
|
||||
|
||||
## 静态方法说明
|
||||
|
||||
### `AudioDecoder.registerDecoder`
|
||||
|
||||
```typescript
|
||||
function registerDecoder(
|
||||
type: AudioType,
|
||||
decoder: new () => AudioDecoder
|
||||
): void;
|
||||
```
|
||||
|
||||
注册自定义解码器到全局解码器系统
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------- | ------------------------ | -------------- |
|
||||
| type | `AudioType` | 音频格式类型 |
|
||||
| decoder | `new () => AudioDecoder` | 解码器构造函数 |
|
||||
|
||||
---
|
||||
|
||||
### `AudioDecoder.decodeAudioData`
|
||||
|
||||
```typescript
|
||||
function decodeAudioData(
|
||||
data: Uint8Array,
|
||||
player: AudioPlayer
|
||||
): Promise<AudioBuffer | null>;
|
||||
```
|
||||
|
||||
核心解码入口方法,自动选择最佳解码方案
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------ | ------------- | ---------------- |
|
||||
| data | `Uint8Array` | 原始音频字节数据 |
|
||||
| player | `AudioPlayer` | 音频播放器实例 |
|
||||
|
||||
**处理流程**:
|
||||
|
||||
1. 通过文件头检测音频类型
|
||||
2. 优先使用浏览器原生解码能力
|
||||
3. 无原生支持时查找注册的自定义解码器
|
||||
4. 返回标准 `AudioBuffer` 格式数据
|
||||
|
||||
---
|
||||
|
||||
## 抽象方法说明
|
||||
|
||||
### `abstract create`
|
||||
|
||||
```typescript
|
||||
function create(): Promise<void>;
|
||||
```
|
||||
|
||||
初始化解码器实例(需分配 WASM 内存等资源)
|
||||
|
||||
---
|
||||
|
||||
### `abstract destroy`
|
||||
|
||||
```typescript
|
||||
function destroy(): void;
|
||||
```
|
||||
|
||||
销毁解码器实例(需释放资源)
|
||||
|
||||
---
|
||||
|
||||
### `abstract decode`
|
||||
|
||||
```typescript
|
||||
function decode(data: Uint8Array): Promise<IAudioDecodeData | undefined>;
|
||||
```
|
||||
|
||||
流式解码方法(分块处理)
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | ------------ | ------------ |
|
||||
| data | `Uint8Array` | 音频数据分块 |
|
||||
|
||||
---
|
||||
|
||||
### `abstract decodeAll`
|
||||
|
||||
```typescript
|
||||
function decodeAll(data: Uint8Array): Promise<IAudioDecodeData | undefined>;
|
||||
```
|
||||
|
||||
全量解码方法(单次处理完整文件)
|
||||
|
||||
---
|
||||
|
||||
### `abstract flush`
|
||||
|
||||
```typescript
|
||||
function flush(): Promise<IAudioDecodeData | undefined>;
|
||||
```
|
||||
|
||||
冲刷解码器缓冲区,获取残留数据
|
||||
|
||||
---
|
||||
|
||||
## 数据结构
|
||||
|
||||
### IAudioDecodeData
|
||||
|
||||
```typescript
|
||||
interface IAudioDecodeData {
|
||||
channelData: Float32Array[]; // 各声道 PCM 数据
|
||||
samplesDecoded: number; // 已解码采样数
|
||||
sampleRate: number; // 采样率 (Hz)
|
||||
errors: IAudioDecodeError[]; // 解码错误集合
|
||||
}
|
||||
```
|
||||
|
||||
## 内置解码器
|
||||
|
||||
- `VorbisDecoder`: 解码 ogg vorbis 音频。
|
||||
- `OpusDecoder`: 解码 ogg opus 音频。
|
207
docs/api/user-client-modules/AudioEffect.md
Normal file
207
docs/api/user-client-modules/AudioEffect.md
Normal file
@ -0,0 +1,207 @@
|
||||
# AudioEffect API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音频处理管道的核心抽象类,为构建音频效果链提供基础框架。所有效果器通过输入/输出节点串联,形成可定制的音频处理流水线。
|
||||
|
||||
---
|
||||
|
||||
## 核心架构
|
||||
|
||||
音频播放流程:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Source --> Effect1
|
||||
Effect1 --> Effect2[...]
|
||||
Effect2 --> GainNode
|
||||
GainNode --> Destination
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 抽象成员说明
|
||||
|
||||
| 成员 | 类型 | 说明 |
|
||||
| -------- | ----------- | -------------------------- |
|
||||
| `input` | `AudioNode` | 效果器输入节点(必须实现) |
|
||||
| `output` | `AudioNode` | 效果器输出节点(必须实现) |
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `connect`
|
||||
|
||||
```typescript
|
||||
function connect(target: IAudioInput, output?: number, input?: number): void;
|
||||
```
|
||||
|
||||
连接至下游音频节点
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------ | ------------- | -------------------------- |
|
||||
| target | `IAudioInput` | 目标效果器/节点 |
|
||||
| output | `number` | 当前效果器输出通道(可选) |
|
||||
| input | `number` | 目标效果器输入通道(可选) |
|
||||
|
||||
---
|
||||
|
||||
### `disconnect`
|
||||
|
||||
```typescript
|
||||
function disconnect(
|
||||
target?: IAudioInput,
|
||||
output?: number,
|
||||
input?: number
|
||||
): void;
|
||||
```
|
||||
|
||||
断开与下游节点的连接
|
||||
|
||||
---
|
||||
|
||||
### `abstract start`
|
||||
|
||||
```typescript
|
||||
function start(): void;
|
||||
```
|
||||
|
||||
效果器激活时调用(可用于初始化参数)
|
||||
|
||||
---
|
||||
|
||||
### `abstract end`
|
||||
|
||||
```typescript
|
||||
function end(): void;
|
||||
```
|
||||
|
||||
效果器停用时调用(可用于资源回收)
|
||||
|
||||
---
|
||||
|
||||
## 自定义效果器示例
|
||||
|
||||
### 混响效果器实现
|
||||
|
||||
```typescript
|
||||
export class ReverbEffect extends AudioEffect {
|
||||
private convolver: ConvolverNode;
|
||||
private dryGain: GainNode;
|
||||
private wetGain: GainNode;
|
||||
|
||||
constructor(ac: AudioContext) {
|
||||
super(ac);
|
||||
|
||||
// 创建节点网络
|
||||
this.dryGain = ac.createGain();
|
||||
this.wetGain = ac.createGain();
|
||||
this.convolver = ac.createConvolver();
|
||||
|
||||
// 定义输入输出
|
||||
this.input = this.dryGain;
|
||||
this.output = this.ac.createGain();
|
||||
|
||||
// 构建处理链
|
||||
this.dryGain.connect(this.output);
|
||||
this.dryGain.connect(this.convolver);
|
||||
this.convolver.connect(this.wetGain);
|
||||
this.wetGain.connect(this.output);
|
||||
}
|
||||
|
||||
/** 设置混响强度 */
|
||||
setMix(value: number) {
|
||||
this.dryGain.gain.value = 1 - value;
|
||||
this.wetGain.gain.value = value;
|
||||
}
|
||||
|
||||
/** 加载脉冲响应 */
|
||||
async loadImpulse(url: string) {
|
||||
const response = await fetch(url);
|
||||
const buffer = await this.ac.decodeAudioData(
|
||||
await response.arrayBuffer()
|
||||
);
|
||||
this.convolver.buffer = buffer;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.output.gain.value = 1;
|
||||
}
|
||||
|
||||
end() {
|
||||
this.output.gain.value = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 内置效果器说明
|
||||
|
||||
### StereoEffect(立体声控制)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input --> Panner[PannerNode]
|
||||
Panner --> Output
|
||||
```
|
||||
|
||||
- 控制声相/3D 空间定位
|
||||
- 支持设置声音方位和位置
|
||||
|
||||
### VolumeEffect(音量控制)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input --> Gain[GainNode]
|
||||
Gain --> Output
|
||||
```
|
||||
|
||||
- 全局音量调节
|
||||
- 支持实时音量渐变
|
||||
|
||||
### ChannelVolumeEffect(多声道控制)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input --> Splitter[ChannelSplitter]
|
||||
Splitter --> Gain1
|
||||
Splitter --> Gain2
|
||||
Gain1 --> Merger
|
||||
Gain2 --> Merger
|
||||
Merger --> Output
|
||||
```
|
||||
|
||||
- 6 声道独立音量控制
|
||||
- 支持环绕声场调节
|
||||
|
||||
### DelayEffect(延迟效果)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input --> Delay[DelayNode]
|
||||
Delay --> Output
|
||||
```
|
||||
|
||||
- 基础延迟效果
|
||||
- 精确到采样级的延迟控制
|
||||
|
||||
### EchoEffect(回声效果)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input --> Gain
|
||||
Gain --> Delay
|
||||
Delay --> Gain[反馈循环]
|
||||
Gain --> Output
|
||||
```
|
||||
|
||||
- 带反馈的延迟效果
|
||||
- 自动渐弱回声处理
|
||||
|
||||
---
|
172
docs/api/user-client-modules/AudioPlayer.md
Normal file
172
docs/api/user-client-modules/AudioPlayer.md
Normal file
@ -0,0 +1,172 @@
|
||||
# AudioPlayer API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音频系统的核心控制器,负责管理音频上下文、路由系统、效果器工厂和全局音频参数。支持多音轨管理和 3D 音频空间化配置。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AudioPlayer --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心架构
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Player[AudioPlayer] --> Sources[音频源工厂]
|
||||
Player --> Effects[效果器工厂]
|
||||
Player --> Routes[路由系统]
|
||||
Player --> Listener[3D 听者配置]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 说明 |
|
||||
| ------------- | ------------------------- | ------------------------ |
|
||||
| `ac` | `AudioContext` | Web Audio API 上下文实例 |
|
||||
| `audioRoutes` | `Map<string, AudioRoute>` | 已注册的音频路由表 |
|
||||
| `gain` | `GainNode` | 全局音量控制节点 |
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
此处暂时只列出方法的简易说明。方法理解难度不高,如果需要可以自行查看代码以及其相关注释来查看如何使用。
|
||||
|
||||
### 音频源工厂方法
|
||||
|
||||
| 方法名 | 返回值 | 说明 |
|
||||
| ----------------------- | -------------------- | ----------------------------- |
|
||||
| `createSource(Source)` | `AudioSource` | 创建自定义音频源 |
|
||||
| `createStreamSource()` | `AudioStreamSource` | 创建流式音频源(直播/长音频) |
|
||||
| `createElementSource()` | `AudioElementSource` | 基于 HTML5 Audio 元素的音源 |
|
||||
| `createBufferSource()` | `AudioBufferSource` | 基于 AudioBuffer 的静态音源 |
|
||||
|
||||
### 效果器工厂方法
|
||||
|
||||
| 方法名 | 返回值 | 说明 |
|
||||
| ----------------------------- | --------------------- | ---------------------------- |
|
||||
| `createEffect(Effect)` | `AudioEffect` | 创建自定义效果器 |
|
||||
| `createVolumeEffect()` | `VolumeEffect` | 全局音量控制器 |
|
||||
| `createStereoEffect()` | `StereoEffect` | 立体声场控制器 |
|
||||
| `createChannelVolumeEffect()` | `ChannelVolumeEffect` | 多声道独立音量控制(6 声道) |
|
||||
| `createDelayEffect()` | `DelayEffect` | 精确延迟效果器 |
|
||||
| `createEchoEffect()` | `EchoEffect` | 回声效果器(带反馈循环) |
|
||||
|
||||
### 路由管理方法
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
| --------------------- | -------------------- | -------------- |
|
||||
| `createRoute(source)` | `AudioSource` | 创建新播放路由 |
|
||||
| `addRoute(id, route)` | `string, AudioRoute` | 注册命名路由 |
|
||||
| `getRoute(id)` | `string` | 获取已注册路由 |
|
||||
| `removeRoute(id)` | `string` | 移除指定路由 |
|
||||
|
||||
### 全局控制方法
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
| ------------------------------- | ------------------------ | -------------------- |
|
||||
| `setVolume(volume)` | `number` (0.0-1.0) | 设置全局音量 |
|
||||
| `getVolume()` | - | 获取当前全局音量 |
|
||||
| `setListenerPosition(x,y,z)` | `number, number, number` | 设置听者 3D 空间坐标 |
|
||||
| `setListenerOrientation(x,y,z)` | `number, number, number` | 设置听者朝向 |
|
||||
| `setListenerUp(x,y,z)` | `number, number, number` | 设置听者头顶朝向 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础音乐播放
|
||||
|
||||
```typescript
|
||||
import { audioPlayer } from '@user/client-modules';
|
||||
|
||||
// 创建音频源(以音频缓冲为例)
|
||||
const bgmSource = audioPlayer.createBufferSource();
|
||||
|
||||
// 创建播放路由
|
||||
const bgmRoute = audioPlayer.createRoute(bgmSource);
|
||||
|
||||
// 添加效果链
|
||||
bgmRoute.addEffect([
|
||||
audioPlayer.createStereoEffect(),
|
||||
audioPlayer.createVolumeEffect()
|
||||
]);
|
||||
|
||||
// 播放控制
|
||||
audioPlayer.play('bgm');
|
||||
audioPlayer.pause('bgm');
|
||||
```
|
||||
|
||||
### 3D 环境音效
|
||||
|
||||
```typescript
|
||||
import { audioPlayer } from '@user/client-modules';
|
||||
|
||||
// 配置3D听者
|
||||
audioPlayer.setListenerPosition(0, 0, 0); // 听者在原点
|
||||
audioPlayer.setListenerOrientation(0, 0, -1); // 面朝屏幕内
|
||||
|
||||
// 创建环境音源
|
||||
const ambientSource = audioPlayer.createBufferSource();
|
||||
await ambientSource.setBuffer(/* 这里填写音频缓冲 */);
|
||||
|
||||
// 配置3D音效路由
|
||||
const ambientRoute = audioPlayer.createRoute(ambientSource);
|
||||
const stereo = audioPlayer.createStereoEffect();
|
||||
stereo.setPosition(5, 2, -3); // 音源位于右前方高处
|
||||
ambientRoute.addEffect(stereo);
|
||||
|
||||
// 循环播放
|
||||
ambientRoute.setLoop(true);
|
||||
audioPlayer.addRoute('ambient', ambientRoute);
|
||||
audioPlayer.play('ambient');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 生命周期管理
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant AudioPlayer
|
||||
participant AudioContext
|
||||
|
||||
User->>AudioPlayer: new AudioPlayer()
|
||||
AudioPlayer->>AudioContext: 创建音频上下文
|
||||
User->>AudioPlayer: createRoute()
|
||||
AudioPlayer->>AudioRoute: 实例化路由
|
||||
User->>AudioPlayer: play()
|
||||
AudioPlayer->>AudioContext: 启动音频时钟
|
||||
loop 播放周期
|
||||
AudioPlayer->>AudioRoute: 更新状态
|
||||
end
|
||||
User->>AudioPlayer: stop()
|
||||
AudioPlayer->>AudioRoute: 释放资源
|
||||
AudioPlayer->>AudioContext: 关闭上下文
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **空间音频配置**
|
||||
3D 效果需统一坐标系:
|
||||
|
||||
```txt
|
||||
(0,0,0) 屏幕中心
|
||||
X+ → 右
|
||||
Y+ ↑ 上
|
||||
Z+ ⊙ 朝向用户
|
||||
```
|
221
docs/api/user-client-modules/AudioRoute.md
Normal file
221
docs/api/user-client-modules/AudioRoute.md
Normal file
@ -0,0 +1,221 @@
|
||||
# AudioRoute 音频播放路由 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音频播放控制的核心类,负责管理音频源与效果器的连接关系,协调播放状态转换,并处理音频管线生命周期。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AudioRoute --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 说明 |
|
||||
| ------------- | ------------------ | ------------------------------------------------ |
|
||||
| `output` | `AudioNode` | 最终输出节点(继承自 IAudioOutput) |
|
||||
| `effectRoute` | `AudioEffect[]` | 效果器链数组(按顺序存储已连接的效果器实例) |
|
||||
| `endTime` | `number` | 淡出过渡时长(单位:秒),默认 0 |
|
||||
| `status` | `AudioStatus` | 当前播放状态(见下方枚举定义) |
|
||||
| `duration` | `number` (getter) | 音频总时长(单位:秒) |
|
||||
| `currentTime` | `number` (get/set) | 当前播放进度(单位:秒),设置时会触发 seek 操作 |
|
||||
|
||||
---
|
||||
|
||||
### AudioStatus 枚举
|
||||
|
||||
```typescript
|
||||
enum AudioStatus {
|
||||
Playing, // 正在播放
|
||||
Pausing, // 淡出暂停过程中
|
||||
Paused, // 已暂停
|
||||
Stoping, // 淡出停止过程中
|
||||
Stoped // 已停止
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
### `setEndTime`
|
||||
|
||||
```typescript
|
||||
function setEndTime(time: number): void;
|
||||
```
|
||||
|
||||
设置淡出过渡时长
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | -------- | ------------------------ |
|
||||
| time | `number` | 淡出动画时长(单位:秒) |
|
||||
|
||||
---
|
||||
|
||||
### `onStart`
|
||||
|
||||
```typescript
|
||||
function onStart(fn?: (route: AudioRoute) => void): void;
|
||||
```
|
||||
|
||||
注册播放开始钩子函数
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | ----------------- | ------------------------ |
|
||||
| `fn` | `(route) => void` | 播放开始时触发的回调函数 |
|
||||
|
||||
---
|
||||
|
||||
### `onEnd`
|
||||
|
||||
```typescript
|
||||
function onEnd(fn?: (time: number, route: AudioRoute) => void): void;
|
||||
```
|
||||
|
||||
注册播放结束钩子函数
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | --------------------------- | --------------------------------------------- |
|
||||
| `fn` | `(duration, route) => void` | 淡出阶段开始时触发的回调,duration 为淡出时长 |
|
||||
|
||||
---
|
||||
|
||||
### `play`
|
||||
|
||||
```typescript
|
||||
function play(when?: number = 0): Promise<void>;
|
||||
```
|
||||
|
||||
启动/恢复音频播放
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------ | -------- | -------------------------------------- |
|
||||
| `when` | `number` | 基于 AudioContext 时间的启动时刻(秒) |
|
||||
|
||||
---
|
||||
|
||||
### `pause`
|
||||
|
||||
```typescript
|
||||
function pause(): Promise<void>;
|
||||
```
|
||||
|
||||
触发暂停流程(执行淡出过渡)
|
||||
|
||||
---
|
||||
|
||||
### `resume`
|
||||
|
||||
```typescript
|
||||
function resume(): void;
|
||||
```
|
||||
|
||||
从暂停状态恢复播放(执行淡入过渡)
|
||||
|
||||
---
|
||||
|
||||
### `stop`
|
||||
|
||||
```typescript
|
||||
function stop(): Promise<void>;
|
||||
```
|
||||
|
||||
完全停止播放并释放资源
|
||||
|
||||
---
|
||||
|
||||
### `addEffect`
|
||||
|
||||
```typescript
|
||||
function addEffect(effect: AudioEffect | AudioEffect[], index?: number): void;
|
||||
```
|
||||
|
||||
添加效果器到处理链
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| -------- | ------------------ | ------------------------------ |
|
||||
| `effect` | `AudioEffect`/数组 | 要添加的效果器实例 |
|
||||
| `index` | `number` (可选) | 插入位置,负数表示从末尾倒计数 |
|
||||
|
||||
---
|
||||
|
||||
### `removeEffect`
|
||||
|
||||
```typescript
|
||||
function removeEffect(effect: AudioEffect): void;
|
||||
```
|
||||
|
||||
从处理链移除效果器
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| -------- | ------------- | ------------------ |
|
||||
| `effect` | `AudioEffect` | 要移除的效果器实例 |
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| -------------- | ---- | ------------------ |
|
||||
| `updateEffect` | - | 效果器链发生变更时 |
|
||||
| `play` | - | 开始/恢复播放时 |
|
||||
| `stop` | - | 完全停止播放后 |
|
||||
| `pause` | - | 进入暂停状态后 |
|
||||
| `resume` | - | 从暂停状态恢复时 |
|
||||
|
||||
---
|
||||
|
||||
## 总使用示例
|
||||
|
||||
```typescript
|
||||
import { audioPlayer } from '@user/client-modules';
|
||||
|
||||
// 创建音频播放器和路由
|
||||
const source = audioPlayer.createBufferSource();
|
||||
const route = audioPlayer.createRoute(audioSource);
|
||||
|
||||
// 配置效果链
|
||||
const stereo = audioPlayer.createStereoEffect();
|
||||
const echo = audioPlayer.createEchoEffect();
|
||||
const volume = audioPlayer.createVolumeEffect();
|
||||
|
||||
route.addEffect([stereo, echo], 0); // 插入到链首
|
||||
route.addEffect(volume); // 音量控制放到链尾
|
||||
|
||||
// 播放暂停
|
||||
await route.play();
|
||||
await route.pause();
|
||||
route.resume(); // 继续操作不是异步,不需要 await
|
||||
await route.stop();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 处理流程示意图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant AudioRoute
|
||||
participant Effects
|
||||
|
||||
User->>AudioRoute: play()
|
||||
AudioRoute->>Effects: 启动所有效果器
|
||||
Effects-->>AudioRoute: 准备完成
|
||||
AudioRoute->>Source: 开始播放
|
||||
loop 播放中
|
||||
AudioRoute->>Effects: 实时音频处理
|
||||
end
|
||||
User->>AudioRoute: pause()
|
||||
AudioRoute->>Effects: 启动淡出过渡
|
||||
Effects-->>AudioRoute: 过渡完成
|
||||
AudioRoute->>Source: 暂停播放
|
||||
```
|
184
docs/api/user-client-modules/AudioSource.md
Normal file
184
docs/api/user-client-modules/AudioSource.md
Normal file
@ -0,0 +1,184 @@
|
||||
# AudioSource API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音频系统的源头抽象类,定义了音频播放的核心控制接口。支持多种音频源类型,包括流媒体、HTML 音频元素和静态音频缓冲。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AudioPlayer --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 抽象成员说明
|
||||
|
||||
| 成员 | 类型 | 说明 |
|
||||
| ------------- | ----------- | ------------------------ |
|
||||
| `output` | `AudioNode` | 音频输出节点(必须实现) |
|
||||
| `duration` | `number` | 音频总时长(秒) |
|
||||
| `currentTime` | `number` | 当前播放时间(秒) |
|
||||
| `playing` | `boolean` | 播放状态标识 |
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `abstract play`
|
||||
|
||||
```typescript
|
||||
function play(when?: number): void;
|
||||
```
|
||||
|
||||
启动音频播放时序
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | -------- | ----------------------------------------------- |
|
||||
| when | `number` | 预定播放时间(基于 `AudioContext.currentTime`) |
|
||||
|
||||
---
|
||||
|
||||
### `abstract stop`
|
||||
|
||||
```typescript
|
||||
function stop(): number;
|
||||
```
|
||||
|
||||
停止播放并返回停止时刻
|
||||
|
||||
---
|
||||
|
||||
### `abstract connect`
|
||||
|
||||
```typescript
|
||||
function connect(target: IAudioInput): void;
|
||||
```
|
||||
|
||||
连接至音频处理管线
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------ | ------------- | ------------------- |
|
||||
| target | `IAudioInput` | 下游处理节点/效果器 |
|
||||
|
||||
---
|
||||
|
||||
### `abstract setLoop`
|
||||
|
||||
```typescript
|
||||
function setLoop(loop: boolean): void;
|
||||
```
|
||||
|
||||
设置循环播放模式
|
||||
|
||||
---
|
||||
|
||||
## 事件系统
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| ------ | ---- | -------------- |
|
||||
| `play` | - | 开始播放时 |
|
||||
| `end` | - | 自然播放结束时 |
|
||||
|
||||
---
|
||||
|
||||
## 自定义音频源示例
|
||||
|
||||
### 网络实时通话源
|
||||
|
||||
```typescript
|
||||
class WebRTCAudioSource extends AudioSource {
|
||||
private mediaStream: MediaStreamAudioSourceNode;
|
||||
output: AudioNode;
|
||||
|
||||
constructor(ac: AudioContext, stream: MediaStream) {
|
||||
super(ac);
|
||||
this.mediaStream = ac.createMediaStreamSource(stream);
|
||||
this.output = this.mediaStream;
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return Infinity;
|
||||
} // 实时流无固定时长
|
||||
get currentTime() {
|
||||
return this.ac.currentTime;
|
||||
}
|
||||
|
||||
play() {
|
||||
this.mediaStream.connect(this.output);
|
||||
this.playing = true;
|
||||
this.emit('play');
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.mediaStream.disconnect();
|
||||
this.playing = false;
|
||||
return this.ac.currentTime;
|
||||
}
|
||||
|
||||
connect(target: IAudioInput) {
|
||||
this.output.connect(target.input);
|
||||
}
|
||||
|
||||
setLoop() {} // 实时流不支持循环
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
|
||||
const source = new WebRTCAudioSource(audioContext, stream);
|
||||
source.connect(effectsChain);
|
||||
source.play();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 内置实现说明
|
||||
|
||||
### AudioStreamSource(流媒体源)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Network[网络数据流] --> Buffer[缓冲区]
|
||||
Buffer --> Decoder[音频解码器]
|
||||
Decoder --> Output[实时音频节点]
|
||||
```
|
||||
|
||||
- 支持渐进式加载
|
||||
- 动态缓冲管理
|
||||
- 适用于浏览器自身不支持的音频类型
|
||||
|
||||
### AudioElementSource(HTML 音频元素源)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AudioTag[audio 元素] -->|音频流| Output[媒体元素源节点]
|
||||
```
|
||||
|
||||
- 基于 HTML5 Audio 元素
|
||||
- 支持跨域资源
|
||||
- 自动处理音频格式兼容
|
||||
|
||||
### AudioBufferSource(静态音频缓冲源)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
File[音频文件] --> Decode[解码为 AudioBuffer]
|
||||
Decode --> Output[缓冲源节点]
|
||||
```
|
||||
|
||||
- 完整音频数据预加载
|
||||
- 精确播放控制
|
||||
- 支持内存音频播放
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **时间精度**
|
||||
所有时间参数均以 `AudioContext.currentTime` 为基准,精度可达 0.01 秒
|
158
docs/api/user-client-modules/BgmController.md
Normal file
158
docs/api/user-client-modules/BgmController.md
Normal file
@ -0,0 +1,158 @@
|
||||
# BgmController API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
BgmController --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
## 类描述
|
||||
|
||||
`BgmController` 是背景音乐系统的核心控制器,支持多 BGM 的加载、音量控制、渐变切换和播放状态管理。继承自 `EventEmitter`,提供完整的音频事件监听机制。
|
||||
|
||||
---
|
||||
|
||||
## 泛型说明
|
||||
|
||||
- `T extends string`: BGM 的唯一标识符类型(默认为项目预定义的 `BgmIds`)
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 描述 |
|
||||
| ---------------- | --------- | ----------------------------------------- |
|
||||
| `prefix` | `string` | BGM 资源路径前缀(默认 `bgms.`) |
|
||||
| `playingBgm` | `T` | 当前正在播放的 BGM ID |
|
||||
| `enabled` | `boolean` | 是否启用音频控制(默认 true) |
|
||||
| `transitionTime` | `number` | 音频切换渐变时长(单位:毫秒,默认 2000) |
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `setTransitionTime`
|
||||
|
||||
```typescript
|
||||
function setTransitionTime(time: number): void;
|
||||
```
|
||||
|
||||
设置所有 BGM 的渐变切换时长。
|
||||
|
||||
- **参数**
|
||||
- `time`: 渐变时长(毫秒)
|
||||
|
||||
---
|
||||
|
||||
### `blockChange`
|
||||
|
||||
```typescript
|
||||
function blockChange(): void;
|
||||
```
|
||||
|
||||
### `unblockChange`
|
||||
|
||||
```typescript
|
||||
function unblockChange(): void;
|
||||
```
|
||||
|
||||
屏蔽/解除屏蔽 BGM 切换(用于特殊场景)。
|
||||
|
||||
---
|
||||
|
||||
### `setVolume`
|
||||
|
||||
```typescript
|
||||
function setVolume(volume: number): void;
|
||||
```
|
||||
|
||||
### `getVolume`
|
||||
|
||||
```typescript
|
||||
function getVolume(): number;
|
||||
```
|
||||
|
||||
控制全局音量(范围 0-1)。
|
||||
|
||||
---
|
||||
|
||||
### `setEnabled`
|
||||
|
||||
```typescript
|
||||
function setEnabled(enabled: boolean): void;
|
||||
```
|
||||
|
||||
启用/禁用整个 BGM 系统(禁用时停止所有播放)。
|
||||
|
||||
---
|
||||
|
||||
### `addBgm`
|
||||
|
||||
```typescript
|
||||
function addBgm(id: T, url?: string): void;
|
||||
```
|
||||
|
||||
加载并注册 BGM 资源。
|
||||
|
||||
- **参数**
|
||||
- `id`: BGM 唯一标识
|
||||
- `url`: 自定义资源路径(默认 `project/bgms/${id}`)
|
||||
|
||||
---
|
||||
|
||||
### `removeBgm`
|
||||
|
||||
```typescript
|
||||
function removeBgm(id: T): void;
|
||||
```
|
||||
|
||||
移除已注册的 BGM 资源。
|
||||
|
||||
---
|
||||
|
||||
### 播放控制方法
|
||||
|
||||
```typescript
|
||||
function play(id: T, when?: number): void; // 播放指定 BGM(带渐变)
|
||||
function pause(): void; // 暂停当前 BGM(保留进度)
|
||||
function resume(): void; // 继续播放当前 BGM
|
||||
function stop(): void; // 停止当前 BGM(重置进度)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| -------- | ---- | ----------------- |
|
||||
| `play` | `[]` | 开始播放新 BGM 时 |
|
||||
| `pause` | `[]` | 暂停播放时 |
|
||||
| `resume` | `[]` | 继续播放时 |
|
||||
| `stop` | `[]` | 完全停止播放时 |
|
||||
|
||||
---
|
||||
|
||||
## 总使用示例
|
||||
|
||||
```typescript
|
||||
import { bgmController } from '@user/client-modules';
|
||||
|
||||
// 设置全局参数
|
||||
bgmCtrl.setTransitionTime(1500);
|
||||
bgmCtrl.setVolume(0.8);
|
||||
|
||||
// 播放控制
|
||||
bgmCtrl.play('battle.mp3'); // 播放战斗BGM
|
||||
bgmCtrl.pause(); // 暂停(如打开菜单)
|
||||
bgmCtrl.resume(); // 继续播放
|
||||
bgmCtrl.play('boss_battle.mp3'); // 切换至BOSS战BGM
|
||||
bgmCtrl.stop(); // 完全停止(如战斗结束)
|
||||
|
||||
// 事件监听
|
||||
bgmCtrl.on('play', () => {
|
||||
console.log('BGM 开始播放:', bgmCtrl.playingBgm);
|
||||
});
|
||||
```
|
147
docs/api/user-client-modules/HeroKeyMover.md
Normal file
147
docs/api/user-client-modules/HeroKeyMover.md
Normal file
@ -0,0 +1,147 @@
|
||||
# HeroKeyMover API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 类描述
|
||||
|
||||
`HeroKeyMover` 是勇士按键移动的核心控制器,负责将热键系统与勇士移动逻辑结合,实现基于键盘输入的连续移动控制。支持多方向优先级处理和移动中断机制。
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 描述 |
|
||||
| ------------ | ----------- | ---------------------------------------- |
|
||||
| `hotkey` | `Hotkey` | 关联的热键控制器实例 |
|
||||
| `mover` | `HeroMover` | 勇士移动逻辑执行器 |
|
||||
| `scope` | `symbol` | 当前移动触发的作用域(默认使用主作用域) |
|
||||
| `hotkeyData` | `MoveKey` | 移动方向与热键的映射配置 |
|
||||
|
||||
---
|
||||
|
||||
## 构造方法
|
||||
|
||||
```typescript
|
||||
function constructor(
|
||||
hotkey: Hotkey,
|
||||
mover: HeroMover,
|
||||
config?: MoveKeyConfig
|
||||
): HeroKeyMover;
|
||||
```
|
||||
|
||||
- **参数**
|
||||
- `hotkey`: 已配置的热键控制器实例
|
||||
- `mover`: 勇士移动逻辑实例
|
||||
- `config`: 自定义方向键映射配置(可选)
|
||||
|
||||
**默认按键映射**:
|
||||
|
||||
```typescript
|
||||
const map = {
|
||||
left: 'moveLeft',
|
||||
right: 'moveRight',
|
||||
up: 'moveUp',
|
||||
down: 'moveDown'
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
### `setScope`
|
||||
|
||||
```typescript
|
||||
function setScope(scope: symbol): void;
|
||||
```
|
||||
|
||||
设置当前移动控制的作用域(用于多场景隔离)。
|
||||
|
||||
- **参数**
|
||||
- `scope`: 唯一作用域标识符
|
||||
|
||||
---
|
||||
|
||||
### `press`
|
||||
|
||||
```typescript
|
||||
function press(dir: Dir): void;
|
||||
```
|
||||
|
||||
触发指定方向的移动按键按下状态。
|
||||
|
||||
- **参数**
|
||||
- `dir`: 移动方向(`'left' | 'right' | 'up' | 'down'`)
|
||||
|
||||
---
|
||||
|
||||
### `release`
|
||||
|
||||
```typescript
|
||||
function release(dir: Dir): void;
|
||||
```
|
||||
|
||||
解除指定方向的移动按键按下状态。
|
||||
|
||||
- **参数**
|
||||
- `dir`: 要释放的移动方向
|
||||
|
||||
---
|
||||
|
||||
### `tryStartMove`
|
||||
|
||||
```typescript
|
||||
function tryStartMove(): boolean;
|
||||
```
|
||||
|
||||
尝试启动移动逻辑(自动根据当前方向键状态判断)。
|
||||
|
||||
- **返回值**
|
||||
`true` 表示移动成功启动,`false` 表示条件不满足
|
||||
|
||||
---
|
||||
|
||||
### `endMove`
|
||||
|
||||
```typescript
|
||||
function endMove(): void;
|
||||
```
|
||||
|
||||
立即终止当前移动过程。
|
||||
|
||||
---
|
||||
|
||||
### `destroy`
|
||||
|
||||
```typescript
|
||||
function destroy(): void;
|
||||
```
|
||||
|
||||
销毁控制器实例(自动解除所有事件监听)。
|
||||
|
||||
---
|
||||
|
||||
## 总使用示例
|
||||
|
||||
```typescript
|
||||
import { gameKey, mainScope } from '@motajs/system-action';
|
||||
|
||||
// 初始化移动控制器
|
||||
const keyMover = new HeroKeyMover(
|
||||
gameKey,
|
||||
heroMover,
|
||||
{ left: 'moveLeft', right: 'moveRight' } // 自定义部分按键映射
|
||||
);
|
||||
|
||||
// 设置允许触发的作用域
|
||||
keyMover.setScope(mainScope);
|
||||
|
||||
// 销毁控制器
|
||||
keyMover.destroy();
|
||||
```
|
||||
|
||||
## 移动优先级机制
|
||||
|
||||
1. **最后按下优先**:当同时按下多个方向键时,以后按下的方向为准
|
||||
2. **队列延续**:在移动过程中持续检测按键状态,自动延续移动队列
|
||||
3. **作用域隔离**:只有当前作用域匹配时才会响应按键事件
|
197
docs/api/user-client-modules/SoundPlayer.md
Normal file
197
docs/api/user-client-modules/SoundPlayer.md
Normal file
@ -0,0 +1,197 @@
|
||||
# SoundPlayer API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
音效管理核心类,提供短音频的加载、播放和空间化控制功能。推荐通过全局单例 `soundPlayer` 使用。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AudioPlayer --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 说明 |
|
||||
| --------- | --------------------- | ---------------------- |
|
||||
| `enabled` | `boolean` | 总开关状态(默认启用) |
|
||||
| `buffer` | `Map<T, AudioBuffer>` | 已加载音效缓冲存储池 |
|
||||
| `playing` | `Set<number>` | 当前活跃音效 ID 集合 |
|
||||
| `gain` | `VolumeEffect` | 全局音量控制器 |
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
### 基础控制
|
||||
|
||||
#### setEnabled
|
||||
|
||||
```typescript
|
||||
function setEnabled(enabled: boolean): void;
|
||||
```
|
||||
|
||||
启用/禁用音效系统(禁用时立即停止所有音效)
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ------- | --------- | ------------ |
|
||||
| enabled | `boolean` | 是否启用音效 |
|
||||
|
||||
---
|
||||
|
||||
#### setVolume / getVolume
|
||||
|
||||
```typescript
|
||||
function setVolume(volume: number): void;
|
||||
function getVolume(): number;
|
||||
```
|
||||
|
||||
全局音量控制(范围 0.0~1.0)
|
||||
|
||||
---
|
||||
|
||||
### 资源管理
|
||||
|
||||
#### add
|
||||
|
||||
```typescript
|
||||
async function add(id: T, data: Uint8Array): Promise<void>;
|
||||
```
|
||||
|
||||
加载并缓存音效资源
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | ------------ | ---------------- |
|
||||
| id | `T` | 音效唯一标识符 |
|
||||
| data | `Uint8Array` | 原始音频字节数据 |
|
||||
|
||||
---
|
||||
|
||||
### 播放控制
|
||||
|
||||
#### play
|
||||
|
||||
```typescript
|
||||
function play(
|
||||
id: T,
|
||||
position?: [number, number, number],
|
||||
orientation?: [number, number, number]
|
||||
): number;
|
||||
```
|
||||
|
||||
播放指定音效(返回音效实例 ID)
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| ----------- | ----------- | --------- | ------------------------- |
|
||||
| id | `T` | - | 音效标识符 |
|
||||
| position | `[x, y, z]` | `[0,0,0]` | 3D 空间坐标(右手坐标系) |
|
||||
| orientation | `[x, y, z]` | `[1,0,0]` | 声音传播方向向量 |
|
||||
|
||||
**坐标系说明**:
|
||||
|
||||
```txt
|
||||
(0,0,0) 听者位置
|
||||
X+ → 右
|
||||
Y+ ↑ 上
|
||||
Z+ ⊙ 朝向听者正前方
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### stop
|
||||
|
||||
```typescript
|
||||
function stop(num: number): void;
|
||||
```
|
||||
|
||||
停止指定音效实例
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
| ---- | -------- | -------------------- |
|
||||
| num | `number` | play() 返回的实例 ID |
|
||||
|
||||
---
|
||||
|
||||
#### stopAllSounds
|
||||
|
||||
```typescript
|
||||
function stopAllSounds(): void;
|
||||
```
|
||||
|
||||
立即停止所有正在播放的音效
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础音效系统
|
||||
|
||||
```typescript
|
||||
import { soundPlayer } from '@user/client-modules';
|
||||
|
||||
// 播放射击音效(右侧声场)
|
||||
const shotId = soundPlayer.play('shoot', [2, 0, 0]);
|
||||
|
||||
// 播放爆炸音效(左后方)
|
||||
soundPlayer.play('explosion', [-3, 0, -2], [-1, 0, -1]);
|
||||
|
||||
// 停止特定音效
|
||||
soundPlayer.stop(shotId);
|
||||
|
||||
// 全局音量控制
|
||||
soundPlayer.setVolume(0.7);
|
||||
```
|
||||
|
||||
### 3D 环境音效
|
||||
|
||||
```typescript
|
||||
// 汽车引擎循环音效
|
||||
let engineSoundId = -1;
|
||||
|
||||
function startEngine() {
|
||||
engineSoundId = soundPlayer.play('engine', [0, 0, -5]);
|
||||
}
|
||||
|
||||
function updateCarPosition(x: number, z: number) {
|
||||
const route = audioPlayer.getRoute(`sounds.${engineSoundId}`);
|
||||
const stereo = route?.effectRoute[0] as StereoEffect;
|
||||
stereo?.setPosition(x, 0, z);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 生命周期管理
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant SoundPlayer
|
||||
participant AudioPlayer
|
||||
|
||||
User->>SoundPlayer: add('explosion', data)
|
||||
SoundPlayer->>AudioPlayer: decodeAudioData()
|
||||
AudioPlayer-->>SoundPlayer: AudioBuffer
|
||||
User->>SoundPlayer: play('explosion')
|
||||
SoundPlayer->>AudioPlayer: 创建路由/效果器
|
||||
AudioPlayer-->>SoundPlayer: 音效ID
|
||||
loop 播放周期
|
||||
SoundPlayer->>AudioPlayer: 更新空间参数
|
||||
end
|
||||
User->>SoundPlayer: stop(id)
|
||||
SoundPlayer->>AudioPlayer: 释放路由资源
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **实例数量限制**
|
||||
同时播放音效建议不超过 32 个,可通过优先级系统管理
|
208
docs/api/user-client-modules/StreamLoader.md
Normal file
208
docs/api/user-client-modules/StreamLoader.md
Normal file
@ -0,0 +1,208 @@
|
||||
# StreamLoader API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
StreamLoader --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
## 类描述
|
||||
|
||||
`StreamLoader` 是流式加载大文件的核心类,支持分块读取网络资源并通过事件机制传递数据。继承自 `EventEmitter`,实现 `IStreamController` 接口,提供流传输控制能力。
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 描述 |
|
||||
| --------- | --------- | ---------------------- |
|
||||
| `url` | `string` | 只读,要加载的资源 URL |
|
||||
| `loading` | `boolean` | 当前是否处于加载状态 |
|
||||
|
||||
---
|
||||
|
||||
## 构造方法
|
||||
|
||||
```typescript
|
||||
function constructor(url: string): StreamLoader;
|
||||
```
|
||||
|
||||
- **参数**
|
||||
- `url`: 要加载的资源地址
|
||||
|
||||
**示例**
|
||||
|
||||
```typescript
|
||||
const loader = new StreamLoader('/api/large-file');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
### `pipe`
|
||||
|
||||
```typescript
|
||||
function pipe(reader: IStreamReader): this;
|
||||
```
|
||||
|
||||
将流数据管道传递给读取器对象。
|
||||
|
||||
- **参数**
|
||||
- `reader`: 实现 `IStreamReader` 接口的对象
|
||||
|
||||
**示例**
|
||||
|
||||
```typescript
|
||||
class MyReader implements IStreamReader {
|
||||
async pump(data, done) {
|
||||
console.log('收到数据块:', data);
|
||||
}
|
||||
// ... 还有一些其他需要实现的方法,参考总是用示例
|
||||
}
|
||||
loader.pipe(new MyReader());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `start`
|
||||
|
||||
```typescript
|
||||
function start(): Promise<void>;
|
||||
```
|
||||
|
||||
启动流传输流程(自动处理分块读取与分发)。
|
||||
|
||||
---
|
||||
|
||||
### `cancel`
|
||||
|
||||
```typescript
|
||||
function cancel(reason?: string): void;
|
||||
```
|
||||
|
||||
终止当前流传输。
|
||||
|
||||
- **参数**
|
||||
- `reason`: 终止原因描述(可选)
|
||||
|
||||
**示例**
|
||||
|
||||
```typescript
|
||||
// 用户取消加载
|
||||
loader.cancel('用户手动取消');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数类型 | 触发时机 |
|
||||
| ------ | --------------------------------- | ------------------------ |
|
||||
| `data` | `data: Uint8Array, done: boolean` | 每接收到一个数据块时触发 |
|
||||
|
||||
**事件监听示例**
|
||||
|
||||
```typescript
|
||||
loader.on('data', (data, done) => {
|
||||
if (done) console.log('传输完成');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 相关接口说明
|
||||
|
||||
### IStreamReader
|
||||
|
||||
```typescript
|
||||
export interface IStreamReader<T = any> {
|
||||
/**
|
||||
* 接受字节流流传输的数据
|
||||
* @param data 传入的字节流数据,只包含本分块的内容
|
||||
* @param done 是否传输完成
|
||||
*/
|
||||
pump(
|
||||
data: Uint8Array | undefined,
|
||||
done: boolean,
|
||||
response: Response
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* 当前对象被传递给加载流时执行的函数
|
||||
* @param controller 传输流控制对象
|
||||
*/
|
||||
piped(controller: IStreamController<T>): void;
|
||||
|
||||
/**
|
||||
* 开始流传输
|
||||
* @param stream 传输流对象
|
||||
* @param controller 传输流控制对象
|
||||
*/
|
||||
start(
|
||||
stream: ReadableStream,
|
||||
controller: IStreamController<T>,
|
||||
response: Response
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* 结束流传输
|
||||
* @param done 是否传输完成,如果为 false 的话,说明可能是由于出现错误导致的终止
|
||||
* @param reason 如果没有传输完成,那么表示失败的原因
|
||||
*/
|
||||
end(done: boolean, reason?: string): void;
|
||||
}
|
||||
```
|
||||
|
||||
- `pump`: 处理每个数据块
|
||||
- `piped`: 当读取器被绑定到流时调用
|
||||
- `start`: 流传输开始时调用
|
||||
- `end`: 流传输结束时调用
|
||||
|
||||
---
|
||||
|
||||
## 总使用示例
|
||||
|
||||
```typescript
|
||||
// 创建流加载器
|
||||
const loader = new StreamLoader('/api/video-stream');
|
||||
|
||||
const videoElement = document.createElement('video');
|
||||
|
||||
// 实现自定义读取器
|
||||
class VideoStreamReader implements IStreamReader {
|
||||
async pump(data, done) {
|
||||
if (data) videoElement.appendBuffer(data);
|
||||
if (done) videoElement.play();
|
||||
}
|
||||
|
||||
piped(controller) {
|
||||
console.log('流传输管道连接成功');
|
||||
}
|
||||
|
||||
start() {
|
||||
console.log('开始流式加载');
|
||||
}
|
||||
|
||||
end() {
|
||||
console.log('流式加载结束');
|
||||
}
|
||||
}
|
||||
|
||||
const reader = new VideoStreamReader();
|
||||
|
||||
// 绑定读取器并启动
|
||||
loader.pipe(reader);
|
||||
loader.start();
|
||||
|
||||
// 监听进度
|
||||
loader.on('data', (_, done) => {
|
||||
if (!done) updateProgressBar();
|
||||
});
|
||||
|
||||
// 错误处理
|
||||
videoElement.onerror = () => loader.cancel('视频解码错误');
|
||||
```
|
157
docs/api/user-client-modules/TextContentParser.md
Normal file
157
docs/api/user-client-modules/TextContentParser.md
Normal file
@ -0,0 +1,157 @@
|
||||
# TextContentParser API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 类描述
|
||||
|
||||
`TextContentParser` 是文字解析核心工具,用于处理文本排版、转义字符解析及动态样式管理。支持自动分词换行、图标嵌入和样式栈控制。
|
||||
|
||||
---
|
||||
|
||||
## 方法说明
|
||||
|
||||
### `parse`
|
||||
|
||||
```typescript
|
||||
function parse(text: string, width: number): ITextContentRenderObject;
|
||||
```
|
||||
|
||||
解析文本并生成渲染数据对象:
|
||||
|
||||
```typescript
|
||||
interface ITextContentRenderObject {
|
||||
lineHeights: number[]; // 每行高度
|
||||
lineWidths: number[]; // 每行宽度
|
||||
data: ITextContentRenderable[]; // 渲染元素集合
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 转义字符语法说明
|
||||
|
||||
### 1. 颜色控制 `\r[color]`
|
||||
|
||||
- **语法**:`\r[颜色值]`
|
||||
- **栈模式**:支持嵌套,用`\r`恢复上一级颜色
|
||||
- **颜色格式**:支持 CSS 颜色字符串
|
||||
|
||||
```typescript
|
||||
// 示例:红→黄→红→默认
|
||||
'\\r[red]危险!\\r[yellow]警告\\r恢复红色\\r默认';
|
||||
```
|
||||
|
||||
### 2. 字号控制 `\c[size]`
|
||||
|
||||
- **语法**:`\c[字号(px)]`
|
||||
- **栈模式**:用`\c`恢复上一级字号
|
||||
|
||||
```typescript
|
||||
// 示例:24px→32px→24px
|
||||
'普通\\c[24]标题\\c[32]超大标题\\c恢复';
|
||||
```
|
||||
|
||||
### 3. 字体家族 `\g[family]`
|
||||
|
||||
- **语法**:`\g[字体名称]`
|
||||
- **栈模式**:用`\g`恢复上一级字体
|
||||
|
||||
```typescript
|
||||
'默认\\g[黑体]中文黑体\\g恢复默认';
|
||||
```
|
||||
|
||||
### 4. 粗体切换 `\d`
|
||||
|
||||
- **语法**:`\d`(开关模式)
|
||||
|
||||
```typescript
|
||||
'正常\\d粗体\\d正常';
|
||||
```
|
||||
|
||||
### 5. 斜体切换 `\e`
|
||||
|
||||
- **语法**:`\e`(开关模式)
|
||||
|
||||
```typescript
|
||||
'正常\\e斜体\\e正常';
|
||||
```
|
||||
|
||||
### 6. 等待间隔 `\z[wait]`
|
||||
|
||||
- **语法**:`\z[等待字符数]`
|
||||
- **计算规则**:`间隔时间 = 字符数 * 当前interval配置`
|
||||
|
||||
```typescript
|
||||
'开始对话\\z[10](暂停500ms)继续';
|
||||
```
|
||||
|
||||
### 7. 图标嵌入 `\i[icon]`
|
||||
|
||||
- **语法**:`\i[图标ID]`
|
||||
- **图标规范**:需预加载到资源管理器
|
||||
|
||||
```typescript
|
||||
'攻击\\i[sword]造成伤害';
|
||||
```
|
||||
|
||||
### 8. 表达式 `${}`
|
||||
|
||||
- **语法**:与模板字符串语法一致,不过是在渲染的时候实时计算,而非字符串声明时计算
|
||||
|
||||
```typescript
|
||||
'${core.status.hero.atk * 10}'; // 显示勇士攻击乘 10
|
||||
'${core.status.hero.atk > 100 ? "高攻击" : "低攻击"}'; // 条件表达式
|
||||
'${(() => { if (a > 10) return 100; else return 10; })()}'; // 嵌入函数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 综合使用示例
|
||||
|
||||
### 战斗伤害提示
|
||||
|
||||
```typescript
|
||||
const text =
|
||||
'\\r[#ff0000]\\c[24]\\d敌人\\i[monster]对你造成\\c[32]\\r[yellow]500\\c\\r伤害!\\z[5]\\d\\e(按空格跳过)';
|
||||
|
||||
const result = parser.parse(text, 400);
|
||||
|
||||
/* 解析结果:
|
||||
[
|
||||
{ type: 'text', color: '#ff0000', size:24, bold:true, text:'敌人' },
|
||||
{ type: 'icon', id:'monster' },
|
||||
{ type: 'text', color:'#ff0000', size:24, text:'对你造成' },
|
||||
{ type: 'text', color:'yellow', size:32, text:'500' },
|
||||
{ type: 'text', color:'#ff0000', size:24, text:'伤害!' },
|
||||
{ type: 'wait', duration:250 }, // 假设 interval=50
|
||||
{ type: 'text', bold:false, italic:true, text:'(按空格跳过)' }
|
||||
]
|
||||
*/
|
||||
```
|
||||
|
||||
### 多语言混合排版
|
||||
|
||||
```typescript
|
||||
const multiLangText =
|
||||
'\\g[Times New Roman]Hello\\g[宋体]你好\\i[globe]\\z[3]\\g切换为\\r[blue]Français';
|
||||
|
||||
// 效果:英文→中文+地球图标→等待→蓝色法文
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **转义字符格式**
|
||||
|
||||
- 必须使用 **双反斜杠**(`\\`)表示转义
|
||||
- 错误示例:`\r[red]`(单反斜杠 `\r` 可能会被识别为换行)
|
||||
- 正确示例:`\\r[red]`
|
||||
|
||||
2. **栈操作规则**
|
||||
|
||||
```typescript
|
||||
// 颜色栈示例
|
||||
'默认\\r[red]红\\r[blue]蓝\\r恢复红\\r恢复默认';
|
||||
// 等效于:push(默认)→push(红)→push(蓝)→pop→pop
|
||||
```
|
245
docs/api/user-client-modules/TextContentTyper.md
Normal file
245
docs/api/user-client-modules/TextContentTyper.md
Normal file
@ -0,0 +1,245 @@
|
||||
# TextContentTyper API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TextContentTyper --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
## 类描述
|
||||
|
||||
`TextContentTyper` 是文字逐字输出(打字机效果)的核心控制器,继承自 `EventEmitter`。用于管理文字排版、渲染时序及样式配置,支持动态修改文本内容和样式。
|
||||
|
||||
---
|
||||
|
||||
## 核心属性说明
|
||||
|
||||
| 属性名 | 类型 | 描述 |
|
||||
| -------- | ----------------------- | ---------------------------------------------- |
|
||||
| `config` | `Required<TyperConfig>` | 当前文字渲染配置(包含字体、行高、对齐等参数) |
|
||||
| `parser` | `TextContentParser` | 文字解析器实例(负责分词、分行等底层处理) |
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `constructor`
|
||||
|
||||
```typescript
|
||||
function constructor(config: Partial<ITextContentConfig>): TextContentTyper;
|
||||
```
|
||||
|
||||
初始化打字机实例,接受文字配置参数:
|
||||
|
||||
```typescript
|
||||
interface ITextContentConfig {
|
||||
/** 字体 */
|
||||
font: Font;
|
||||
/** 是否持续上一次的文本,开启后,如果修改后的文本以修改前的文本为开头,那么会继续播放而不会从头播放(暂未实现,后续更新) */
|
||||
keepLast: boolean;
|
||||
/** 打字机时间间隔,即两个字出现之间相隔多长时间 */
|
||||
interval: number;
|
||||
/** 行高 */
|
||||
lineHeight: number;
|
||||
/** 分词规则 */
|
||||
wordBreak: WordBreak;
|
||||
/** 文字对齐方式 */
|
||||
textAlign: TextAlign;
|
||||
/** 行首忽略字符,即不会出现在行首的字符 */
|
||||
ignoreLineStart: Iterable<string>;
|
||||
/** 行尾忽略字符,即不会出现在行尾的字符 */
|
||||
ignoreLineEnd: Iterable<string>;
|
||||
/** 会被分词规则识别的分词字符 */
|
||||
breakChars: Iterable<string>;
|
||||
/** 填充样式 */
|
||||
fillStyle: CanvasStyle;
|
||||
/** 描边样式 */
|
||||
strokeStyle: CanvasStyle;
|
||||
/** 线宽 */
|
||||
strokeWidth: number;
|
||||
/** 文字宽度,到达这么宽之后换行 */
|
||||
width: number;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `setConfig`
|
||||
|
||||
```typescript
|
||||
function setConfig(config: Partial<ITextContentConfig>): void;
|
||||
```
|
||||
|
||||
动态更新配置参数(支持部分更新)
|
||||
|
||||
---
|
||||
|
||||
### `setText`
|
||||
|
||||
```typescript
|
||||
function setText(text: string): void;
|
||||
```
|
||||
|
||||
设置要显示的文本内容(自动重置播放进度)
|
||||
|
||||
---
|
||||
|
||||
### `type`
|
||||
|
||||
```typescript
|
||||
function type(): void;
|
||||
```
|
||||
|
||||
启动逐字显示效果
|
||||
|
||||
---
|
||||
|
||||
### `typeAll`
|
||||
|
||||
```typescript
|
||||
function typeAll(): void;
|
||||
```
|
||||
|
||||
立即完整显示所有文字
|
||||
|
||||
---
|
||||
|
||||
### `setRender`
|
||||
|
||||
```typescript
|
||||
function setRender(render: TyperFunction): void;
|
||||
```
|
||||
|
||||
设置自定义渲染逻辑:
|
||||
|
||||
```typescript
|
||||
type TyperFunction = (
|
||||
data: TyperRenderable[], // 待渲染元素
|
||||
typing: boolean // 是否正在播放中
|
||||
) => void;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| ----------- | ---- | ------------------ |
|
||||
| `typeStart` | `[]` | 开始逐字显示时 |
|
||||
| `typeEnd` | `[]` | 全部文字显示完成时 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法 - 对话框文字
|
||||
|
||||
```typescript
|
||||
// 初始化配置
|
||||
const typer = new TextContentTyper({
|
||||
font: new Font('黑体', 18),
|
||||
interval: 50,
|
||||
lineHeight: 1.2,
|
||||
width: 400
|
||||
});
|
||||
|
||||
// 设置文本内容
|
||||
typer.setText(`「这是逐字显示的文字效果...
|
||||
第二行会自动换行」`);
|
||||
|
||||
// 注册渲染逻辑
|
||||
typer.setRender((elements, isTyping) => {
|
||||
elements.forEach(element => {
|
||||
if (element.type === TextContentType.Text) {
|
||||
drawText(element.x, element.y, element.text);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 开始播放
|
||||
typer.type();
|
||||
```
|
||||
|
||||
### 动态样式修改
|
||||
|
||||
```typescript
|
||||
// 修改为红色斜体
|
||||
typer.setConfig({
|
||||
font: new Font('楷体', 20),
|
||||
fillStyle: '#ff0000'
|
||||
});
|
||||
|
||||
// 修改播放速度
|
||||
typer.setConfig({ interval: 30 });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 底层数据结构
|
||||
|
||||
### 渲染元素类型
|
||||
|
||||
```typescript
|
||||
type TyperRenderable =
|
||||
| TyperTextRenderable // 文本元素
|
||||
| TyperIconRenderable // 图标元素
|
||||
| TyperWaitRenderable; // 等待间隔
|
||||
```
|
||||
|
||||
::: code-group
|
||||
|
||||
```ts [TyperTextRenderable]
|
||||
interface TyperTextRenderable {
|
||||
type: TextContentType.Text;
|
||||
x: number;
|
||||
y: number;
|
||||
text: string;
|
||||
font: string;
|
||||
fillStyle: CanvasStyle;
|
||||
strokeStyle: CanvasStyle;
|
||||
/** 文字画到哪个索引 */
|
||||
pointer: number;
|
||||
/** 这段文字的总高度 */
|
||||
height: number;
|
||||
}
|
||||
```
|
||||
|
||||
```ts [TyperIconRenderable]
|
||||
interface TyperIconRenderable {
|
||||
type: TextContentType.Icon;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
renderable: RenderableData | AutotileRenderable;
|
||||
}
|
||||
```
|
||||
|
||||
```ts [TyperWaitRenderable]
|
||||
interface TyperWaitRenderable {
|
||||
type: TextContentType.Wait;
|
||||
wait: number;
|
||||
waited: number;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **性能优化**
|
||||
当处理长文本(>1000 字)时,建议预调用 `parser.parse()` 进行分页
|
||||
|
||||
2. **坐标系统**
|
||||
所有坐标基于初始化时设置的 `width` 参数进行相对计算
|
||||
|
||||
3. **动态修改限制**
|
||||
在播放过程中修改配置可能导致渲染异常,建议在 `typeEnd` 事件后操作
|
||||
|
||||
4. **使用场景**
|
||||
本接口的使用场景并不多,建议使用 `TextContent` 组件。如果必须使用的话,可以直接阅读源码来看一些实现细节。
|
166
docs/api/user-client-modules/TextboxStore.md
Normal file
166
docs/api/user-client-modules/TextboxStore.md
Normal file
@ -0,0 +1,166 @@
|
||||
# TextboxStore API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TextboxStore --> EventEmitter
|
||||
|
||||
click EventEmitter "https://nodejs.org/api/events.html#class-eventemitter"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
`TextboxStore` 是文本框的集中管理器,继承自 `EventEmitter`。所有 `Textbox` 组件实例化时会自动注册到该类的静态 `list` 中,支持通过 ID 精准控制特定文本框。
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `TextboxStore.get`
|
||||
|
||||
```typescript
|
||||
function get(id: string): TextboxStore | undefined;
|
||||
```
|
||||
|
||||
**静态方法**:通过 ID 获取已注册的文本框控制器
|
||||
|
||||
- **参数**
|
||||
`id`: 文本框的唯一标识符
|
||||
- **返回值**
|
||||
找到返回实例,否则返回 `undefined`
|
||||
|
||||
---
|
||||
|
||||
### `setText`
|
||||
|
||||
```typescript
|
||||
function setText(text: string): void;
|
||||
```
|
||||
|
||||
**动态更新文本内容**
|
||||
|
||||
- **特性**
|
||||
- 自动重置打字机进度
|
||||
- 触发重新排版计算
|
||||
|
||||
---
|
||||
|
||||
### `modify`
|
||||
|
||||
```typescript
|
||||
function modify(data: Partial<TextboxProps>): void;
|
||||
```
|
||||
|
||||
**动态修改文本框配置**
|
||||
|
||||
- **参数**
|
||||
`data`: 需更新的属性(支持所有 `TextboxProps` 属性)
|
||||
|
||||
---
|
||||
|
||||
### `endType`
|
||||
|
||||
```typescript
|
||||
function endType(): void;
|
||||
```
|
||||
|
||||
**立即结束打字机动画**
|
||||
|
||||
- **特性**
|
||||
- 强制显示全部文本
|
||||
- 触发 `typeEnd` 事件
|
||||
|
||||
---
|
||||
|
||||
### `show`
|
||||
|
||||
```ts
|
||||
function show(): void;
|
||||
```
|
||||
|
||||
### `hide`
|
||||
|
||||
```ts
|
||||
function hide(): void;
|
||||
```
|
||||
|
||||
控制文本框的显示和隐藏。
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 跨场景更新对话内容
|
||||
|
||||
```typescript
|
||||
// 在剧情管理器中的调用
|
||||
const updateChapterDialog = (chapterId: string) => {
|
||||
const store = TextboxStore.get(`chapter_${chapterId}`);
|
||||
store?.setText(getChapterText(chapterId));
|
||||
store?.modify({ title: `第 ${chapterId} 章` });
|
||||
};
|
||||
```
|
||||
|
||||
### 紧急提示打断当前动画
|
||||
|
||||
```typescript
|
||||
// 强制显示关键信息
|
||||
const showEmergencyAlert = () => {
|
||||
const alertBox = TextboxStore.get('system_alert');
|
||||
alertBox?.setText('警告!基地即将爆炸!');
|
||||
alertBox?.endType(); // 跳过打字动画
|
||||
alertBox?.show();
|
||||
};
|
||||
```
|
||||
|
||||
### 动态样式调整
|
||||
|
||||
```typescript
|
||||
// 根据昼夜切换对话框样式
|
||||
const updateDialogStyle = (isNight: boolean) => {
|
||||
TextboxStore.list.forEach(store => {
|
||||
store.modify({
|
||||
backColor: isNight ? '#1A1A32' : '#F0F0FF',
|
||||
titleFill: isNight ? '#E6E6FA' : '#2F4F4F'
|
||||
});
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **ID 管理规范**
|
||||
建议显式指定可预测的 ID 格式:
|
||||
|
||||
```tsx
|
||||
// 创建时指定可追踪 ID
|
||||
<Textbox id={`npc_${npcId}_dialog`} ... />
|
||||
```
|
||||
|
||||
2. **未找到实例处理**
|
||||
调用前需做空值检测:
|
||||
|
||||
```typescript
|
||||
const store = TextboxStore.get('custom_id');
|
||||
if (!store) {
|
||||
console.warn('文本框未注册: custom_id');
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
3. **生命周期匹配**
|
||||
在组件卸载时自动注销实例,请勿持有长期引用
|
||||
|
||||
4. **批量操作优化**
|
||||
同时操作多个实例时建议使用迭代器:
|
||||
```typescript
|
||||
// 隐藏所有对话框
|
||||
TextboxStore.list.forEach(store => store.hide());
|
||||
```
|
147
docs/api/user-client-modules/TipStore.md
Normal file
147
docs/api/user-client-modules/TipStore.md
Normal file
@ -0,0 +1,147 @@
|
||||
# TipStore API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 类描述
|
||||
|
||||
`TipStore` 是提示框的集中管理器,提供全局访问和控制提示组件的能力。所有通过 `<Tip>` 组件注册的实例会自动加入静态 `list` 容器,支持通过 ID 精准控制特定提示框。
|
||||
|
||||
---
|
||||
|
||||
## 核心方法说明
|
||||
|
||||
### `TipStore.get`
|
||||
|
||||
```typescript
|
||||
function get(id: string): TipStore | undefined;
|
||||
```
|
||||
|
||||
**静态方法**:通过 ID 获取已注册的提示框控制器
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---- | -------- | ---- | ---------------- |
|
||||
| `id` | `string` | 是 | 提示框的唯一标识 |
|
||||
|
||||
**返回值**:找到返回实例,否则返回 `undefined`
|
||||
|
||||
---
|
||||
|
||||
### `TipStore.use`
|
||||
|
||||
```typescript
|
||||
function use(id: string, data: TipExpose): TipStore;
|
||||
```
|
||||
|
||||
**静态方法**:注册提示框实例到全局管理器(通常在组件内部使用)
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ------ | ----------- | ---- | ----------------------- |
|
||||
| `id` | `string` | 是 | 提示框的唯一标识 |
|
||||
| `data` | `TipExpose` | 是 | 来自 Tip 组件的暴露接口 |
|
||||
|
||||
---
|
||||
|
||||
### `drawTip`
|
||||
|
||||
```typescript
|
||||
function drawTip(text: string, icon?: AllIds | AllNumbers): void;
|
||||
```
|
||||
|
||||
**显示提示内容**(支持带图标的提示)
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ------ | ---------------------- | ---- | ------------------------------- |
|
||||
| `text` | `string` | 是 | 提示文字内容 |
|
||||
| `icon` | `AllIds \| AllNumbers` | 否 | 图标资源 ID(字符串或数字形式) |
|
||||
|
||||
**特性**:
|
||||
|
||||
- 自动触发淡入动画
|
||||
- 3 秒无操作后自动淡出
|
||||
- 重复调用会重置计时器
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础提示
|
||||
|
||||
```typescript
|
||||
// 获取预先注册的提示框
|
||||
const tip = TipStore.get('item-get-tip');
|
||||
|
||||
// 显示纯文本提示
|
||||
tip?.drawTip('获得金币 x100');
|
||||
|
||||
// 显示带图标的提示
|
||||
tip?.drawTip('获得 传说之剑', 'legend_sword');
|
||||
```
|
||||
|
||||
### 全局广播提示
|
||||
|
||||
```typescript
|
||||
// 向所有提示框发送通知
|
||||
TipStore.list.forEach(store => {
|
||||
store.drawTip('系统将在5分钟后维护', 'warning');
|
||||
});
|
||||
```
|
||||
|
||||
### 动态内容提示
|
||||
|
||||
```typescript
|
||||
// 组合动态内容
|
||||
const showDamageTip = (damage: number) => {
|
||||
TipStore.get('combat-tip')?.drawTip(
|
||||
`造成 ${damage} 点伤害`,
|
||||
damage > 1000 ? 'critical_hit' : 'normal_hit'
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 生命周期管理
|
||||
|
||||
### 组件注册流程
|
||||
|
||||
```tsx
|
||||
// 在组件定义时注册实例
|
||||
<Tip id="quest-tip" loc={[20, 20, 400, 40]}></Tip>;
|
||||
|
||||
// 在业务逻辑中调用
|
||||
const showQuestComplete = () => {
|
||||
TipStore.get('quest-tip')?.drawTip('任务「勇者的试炼」完成!');
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **自动清理机制**
|
||||
组件卸载时自动注销实例,跨场景访问时需确保目标组件已挂载
|
||||
|
||||
2. **错误处理**
|
||||
建议封装安全访问方法:
|
||||
|
||||
```typescript
|
||||
const safeDrawTip = (id: string, text: string) => {
|
||||
const instance = TipStore.get(id);
|
||||
if (!instance) {
|
||||
console.error(`Tip ${id} not registered`);
|
||||
return;
|
||||
}
|
||||
instance.drawTip(text);
|
||||
};
|
||||
```
|
||||
|
||||
3. **动画队列**
|
||||
连续调用时会中断当前动画,建议重要提示添加延迟:
|
||||
```typescript
|
||||
tip.drawTip('第一条提示');
|
||||
setTimeout(() => {
|
||||
tip.drawTip('第二条重要提示');
|
||||
}, 3200); // 等待淡出动画结束
|
||||
```
|
217
docs/api/user-client-modules/WeatherController.md
Normal file
217
docs/api/user-client-modules/WeatherController.md
Normal file
@ -0,0 +1,217 @@
|
||||
# WeatherController API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
WeatherController --> IWeather
|
||||
```
|
||||
|
||||
_实现 `IWeather` 接口_
|
||||
|
||||
## 类描述
|
||||
|
||||
`WeatherController` 是天气系统的核心控制器,支持动态管理多种天气效果(如雨、雪、雾等),可将天气效果绑定到任意渲染元素上,实现多场景独立天气控制。
|
||||
|
||||
---
|
||||
|
||||
## 属性说明
|
||||
|
||||
| 属性名 | 类型 | 描述 |
|
||||
| -------------- | -------------------------------- | ----------------------------------------------------------------- |
|
||||
| `id` | `string` | 只读,控制器的唯一标识符 |
|
||||
| `active` | `Set<IWeather>` | 当前激活的天气实例集合 |
|
||||
| `list`(静态) | `Map<string, Weather>` | 静态属性,存储所有注册的天气类型(键为天气 ID,值为天气构造函数) |
|
||||
| `map`(静态) | `Map<string, WeatherController>` | 静态属性,存储所有控制器实例 |
|
||||
|
||||
---
|
||||
|
||||
## 构造方法
|
||||
|
||||
```typescript
|
||||
function constructor(id: string): WeatherController;
|
||||
```
|
||||
|
||||
- **参数**
|
||||
- `id`: 控制器的标识符
|
||||
|
||||
## 方法说明
|
||||
|
||||
### `activate`
|
||||
|
||||
```typescript
|
||||
function activate(id: string, level?: number): IWeather | undefined;
|
||||
```
|
||||
|
||||
激活指定天气。
|
||||
|
||||
- **参数**
|
||||
- `id`: 已注册的天气 ID
|
||||
- `level`: 天气强度等级(可选)
|
||||
|
||||
---
|
||||
|
||||
### `bind`
|
||||
|
||||
```typescript
|
||||
function bind(item?: RenderItem): void;
|
||||
```
|
||||
|
||||
绑定/解绑渲染元素。
|
||||
|
||||
- **参数**
|
||||
- `item`: 要绑定的渲染元素(不传则解绑)
|
||||
|
||||
---
|
||||
|
||||
### `deactivate`
|
||||
|
||||
```typescript
|
||||
function deactivate(weather: IWeather): void;
|
||||
```
|
||||
|
||||
关闭指定天气效果。
|
||||
|
||||
---
|
||||
|
||||
### `clearWeather`
|
||||
|
||||
```typescript
|
||||
function clearWeather(): void;
|
||||
```
|
||||
|
||||
清空所有天气效果。
|
||||
|
||||
---
|
||||
|
||||
### `getWeather`
|
||||
|
||||
```typescript
|
||||
function getWeather<T extends IWeather = IWeather>(
|
||||
weather: new (level?: number) => T
|
||||
): T | null;
|
||||
```
|
||||
|
||||
获取指定天气实例。
|
||||
|
||||
**示例**
|
||||
|
||||
```ts
|
||||
import { RainWeather } from '@user/client-modules';
|
||||
|
||||
const rain = controller.getWeather(RainWeather);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `destroy`
|
||||
|
||||
```typescript
|
||||
function destroy(): void;
|
||||
```
|
||||
|
||||
摧毁这个天气控制器,摧毁后不可继续使用。
|
||||
|
||||
---
|
||||
|
||||
## 静态方法说明
|
||||
|
||||
### `WeatherController.register`
|
||||
|
||||
```typescript
|
||||
function register(id: string, weather: Weather): void;
|
||||
```
|
||||
|
||||
**静态方法**:注册新的天气类型。
|
||||
|
||||
- **参数**
|
||||
- `id`: 天气唯一标识(如 "rain")
|
||||
- `weather`: 天气类(需实现 `IWeather` 接口)
|
||||
|
||||
---
|
||||
|
||||
### `WeatherController.get`
|
||||
|
||||
```typescript
|
||||
function get(id: string): WeatherController | undefined;
|
||||
```
|
||||
|
||||
- **参数**
|
||||
- `id`: 要获得的控制器标识符
|
||||
|
||||
## 天气接口说明
|
||||
|
||||
```typescript
|
||||
interface IWeather {
|
||||
activate(item: RenderItem): void; // 初始化天气效果
|
||||
frame(): void; // 每帧更新逻辑
|
||||
deactivate(item: RenderItem): void; // 清除天气效果
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 内置天气
|
||||
|
||||
- `rain`: 下雨天气
|
||||
|
||||
## 总使用示例 实现滤镜天气效果
|
||||
|
||||
::: code-group
|
||||
|
||||
```typescript [定义天气]
|
||||
// 定义灰度滤镜天气
|
||||
class GrayFilterWeather implements IWeather {
|
||||
private scale: number;
|
||||
private now: number = 0;
|
||||
private item?: RenderItem;
|
||||
|
||||
constructor(level: number = 5) {
|
||||
this.scale = level / 10;
|
||||
}
|
||||
|
||||
activate(item: RenderItem) {
|
||||
// 添加灰度滤镜
|
||||
item.filter = `grayscale(0)`;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
frame() {
|
||||
// 动态调整滤镜强度(示例:正弦波动)
|
||||
if (this.item) {
|
||||
const intensity = ((Math.sin(Date.now() / 1000) + 1) * scale) / 2;
|
||||
this.item.filter = `grayscale(${itensity})`;
|
||||
}
|
||||
}
|
||||
|
||||
deactivate(item: RenderItem) {
|
||||
item.filter = `none`;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册天气类型
|
||||
WeatherController.register('gray-filter', GrayFilterWeather);
|
||||
```
|
||||
|
||||
```tsx [使用天气]
|
||||
import { defineComponent, onMounted } from 'vue';
|
||||
import { Container } from '@motajs/render';
|
||||
import { useWeather } from '@user/client-modules';
|
||||
|
||||
const MyCom = defineComponent(() => {
|
||||
const [controller] = useWeather();
|
||||
|
||||
const root = ref<Container>();
|
||||
|
||||
onMounted(() => {
|
||||
// 绑定天气的渲染元素
|
||||
controller.bind(root.value);
|
||||
// 激活天气效果
|
||||
controller.activate('gray-filter', 5);
|
||||
});
|
||||
|
||||
return () => <container ref={root}></container>;
|
||||
});
|
||||
```
|
||||
|
||||
:::
|
410
docs/api/user-client-modules/functions.md
Normal file
410
docs/api/user-client-modules/functions.md
Normal file
@ -0,0 +1,410 @@
|
||||
# 模块函数 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 钩子
|
||||
|
||||
### `onOrientationChange`
|
||||
|
||||
```typescript
|
||||
function onOrientationChange(hook: OrientationHook): void;
|
||||
```
|
||||
|
||||
监听屏幕方向变化事件。
|
||||
**参数**
|
||||
|
||||
- `hook`: 方向变化回调函数
|
||||
|
||||
```typescript
|
||||
type OrientationHook = (
|
||||
orientation: Orientation, // 当前方向
|
||||
width: number, // 窗口宽度
|
||||
height: number // 窗口高度
|
||||
) => void;
|
||||
```
|
||||
|
||||
**示例** - 响应式布局
|
||||
|
||||
```typescript
|
||||
import { onOrientationChange, Orientation } from './use';
|
||||
|
||||
onOrientationChange((orient, width) => {
|
||||
if (orient === Orientation.Portrait) {
|
||||
// 竖屏模式
|
||||
adjustMobileLayout(width);
|
||||
} else {
|
||||
// 横屏模式
|
||||
resetDesktopLayout();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `onLoaded`
|
||||
|
||||
```typescript
|
||||
function onLoaded(hook: () => void): void;
|
||||
```
|
||||
|
||||
在游戏核心资源加载完成后执行回调(若已加载则立即执行)。
|
||||
|
||||
---
|
||||
|
||||
## 过渡动画控制
|
||||
|
||||
### 通用接口
|
||||
|
||||
```typescript
|
||||
interface ITransitionedController<T> {
|
||||
readonly ref: Ref<T>; // 响应式引用
|
||||
readonly value: T; // 当前值
|
||||
set(value: T, time?: number): void; // 设置目标值
|
||||
mode(timing: TimingFn): void; // 设置缓动曲线
|
||||
setTime(time: number): void; // 设置默认时长
|
||||
}
|
||||
```
|
||||
|
||||
### `transitioned`
|
||||
|
||||
```typescript
|
||||
function transitioned(
|
||||
value: number, // 初始值
|
||||
time: number, // 默认过渡时长(ms)
|
||||
curve: TimingFn // 缓动函数(如 linear())
|
||||
): ITransitionedController<number> | null;
|
||||
```
|
||||
|
||||
创建数值渐变控制器(仅限组件内使用)。
|
||||
|
||||
**示例** - 旋转动画
|
||||
|
||||
```tsx
|
||||
// Vue 组件内
|
||||
const rotate = transitioned(0, 500, hyper('sin', 'out'));
|
||||
|
||||
// 触发动画
|
||||
rotate.set(Math.PI, 800); // 800ms 内旋转到 180 度
|
||||
|
||||
// 模板中使用
|
||||
<text rotate={rotate.ref.value} text="一些显示内容" />;
|
||||
```
|
||||
|
||||
### `transitionedColor`
|
||||
|
||||
```typescript
|
||||
function transitionedColor(
|
||||
color: string, // 初始颜色(目前支持 #RGB/#RGBA/rgb()/rgba())
|
||||
time: number, // 默认过渡时长(ms)
|
||||
curve: TimingFn // 缓动函数
|
||||
): ITransitionedController<string> | null;
|
||||
```
|
||||
|
||||
创建颜色渐变控制器(仅限组件内使用)。
|
||||
|
||||
**示例** - 背景色过渡
|
||||
|
||||
```tsx
|
||||
// Vue 组件内
|
||||
const bgColor = transitionedColor('#fff', 300, linear());
|
||||
|
||||
// 触发颜色变化
|
||||
bgColor.set('rgba(255, 0, 0, 0.5)'); // 渐变为半透明红色
|
||||
|
||||
// 模板中使用
|
||||
<g-rect fillStyle={bgColor.ref.value} />;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **组件生命周期**:过渡控制器必须在 Vue 组件内部创建,卸载时自动销毁
|
||||
2. **性能优化**:避免在频繁触发的回调(如每帧渲染)中创建新控制器
|
||||
3. **颜色格式**:`transitionedColor` 支持 HEX/RGB/RGBA,但不支持 HSL
|
||||
4. **默认时长**:调用 `set()` 时不传时间参数则使用初始化时设置的时间
|
||||
|
||||
### 高级用法示例
|
||||
|
||||
#### 组合动画
|
||||
|
||||
```typescript
|
||||
// 同时控制位置和透明度
|
||||
const posX = transitioned(0, 500, linear());
|
||||
const alpha = transitioned(1, 300, linear());
|
||||
|
||||
const moveAndFade = () => {
|
||||
posX.set(200);
|
||||
alpha.set(0);
|
||||
};
|
||||
|
||||
// 组件卸载时自动清理动画资源
|
||||
```
|
||||
|
||||
## 组件控制
|
||||
|
||||
### `getConfirm`
|
||||
|
||||
```typescript
|
||||
function getConfirm(
|
||||
controller: IUIMountable, // UI 控制器
|
||||
text: string, // 确认内容
|
||||
loc: ElementLocator, // 定位配置
|
||||
width: number, // 对话框宽度(像素)
|
||||
props?: Partial<ConfirmBoxProps> // 扩展配置
|
||||
): Promise<boolean>;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
| ------------ | -------------------------- | ---- | ---------------------------------------------- |
|
||||
| `controller` | `IUIMountable` | 是 | UI 控制器实例(通常从组件 props 获取) |
|
||||
| `text` | `string` | 是 | 需要用户确认的文本内容 |
|
||||
| `loc` | `ElementLocator` | 是 | 对话框位置配置(需包含 x,y 坐标及锚点) |
|
||||
| `width` | `number` | 是 | 对话框宽度(像素),高度自动计算 |
|
||||
| `props` | `Partial<ConfirmBoxProps>` | 否 | 扩展配置项(支持所有 ConfirmBox 组件的 props) |
|
||||
|
||||
---
|
||||
|
||||
#### 返回值
|
||||
|
||||
返回 `Promise<boolean>`:
|
||||
|
||||
- `true` 表示用户点击确认
|
||||
- `false` 表示用户取消或关闭
|
||||
|
||||
---
|
||||
|
||||
#### 使用示例
|
||||
|
||||
##### 基础用法 - 删除确认
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { DefaultProps } from '@motajs/render';
|
||||
import { GameUI } from '@motajs/system-ui';
|
||||
|
||||
// 在业务逻辑中调用,注意,组件需要使用 UI 控制器打开,它会自动传递 controller 参数
|
||||
const MyCom = defineComponent<DefaultProps>(props => {
|
||||
const handleDeleteItem = async (itemId: string) => {
|
||||
const confirmed = await getConfirm(
|
||||
props.controller, // 从组件 props 获取控制器
|
||||
`确认删除 ID 为 ${itemId} 的项目吗?`,
|
||||
[208, 208, void 0, void 0, 0.5, 0.5], // 居中显示
|
||||
208
|
||||
);
|
||||
|
||||
if (confirmed) {
|
||||
api.deleteItem(itemId);
|
||||
}
|
||||
};
|
||||
|
||||
return () => (
|
||||
<container>
|
||||
{/* 假设有一个按钮在点击后触发上面的删除函数 */}
|
||||
<text text="删除" onClick={() => handleDeleteItem(item.id)} />
|
||||
</container>
|
||||
);
|
||||
});
|
||||
|
||||
export const MyUI = new GameUI('my-ui', MyCom);
|
||||
```
|
||||
|
||||
##### 自定义按钮文本
|
||||
|
||||
```typescript
|
||||
import { mainUIController } from '@user/client-modules';
|
||||
// 注意,如果在 client-modules/render/ui 下编写代码,应该引入:
|
||||
import { mainUIController } from './controller.ts';
|
||||
|
||||
// 修改确认/取消按钮文案
|
||||
const result = await getConfirm(
|
||||
// 传入主 UI 控制器也可以
|
||||
mainUIController,
|
||||
'切换场景将丢失未保存进度',
|
||||
[208, 208, void 0, void 0, 0.5, 0.5],
|
||||
320,
|
||||
{
|
||||
yesText: '继续切换',
|
||||
noText: '留在当前',
|
||||
selFill: '#e74c3c',
|
||||
border: '#c0392b'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `getChoice`
|
||||
|
||||
```typescript
|
||||
function getChoice<T extends ChoiceKey = ChoiceKey>(
|
||||
controller: IUIMountable, // UI 控制器
|
||||
choices: ChoiceItem[], // 选项数组
|
||||
loc: ElementLocator, // 定位配置
|
||||
width: number, // 对话框宽度(像素)
|
||||
props?: Partial<ChoicesProps> // 扩展配置
|
||||
): Promise<T>;
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 |
|
||||
| ------------ | ----------------------- | ---- | ------------------------------------------- |
|
||||
| `controller` | `IUIMountable` | 是 | UI 控制器实例(通常从组件 props 获取) |
|
||||
| `choices` | `ChoiceItem[]` | 是 | 选项数组,格式为 `[key, text]` 的元组 |
|
||||
| `loc` | `ElementLocator` | 是 | 对话框位置配置(需包含 x,y 坐标及锚点) |
|
||||
| `width` | `number` | 是 | 对话框宽度(像素),高度自动计算 |
|
||||
| `props` | `Partial<ChoicesProps>` | 否 | 扩展配置项(支持所有 Choices 组件的 props) |
|
||||
|
||||
#### 返回值
|
||||
|
||||
返回 `Promise<T>`:
|
||||
|
||||
- 解析为选中项的 `key` 值
|
||||
|
||||
#### 使用示例
|
||||
|
||||
##### 基础用法 - 难度选择
|
||||
|
||||
```typescript
|
||||
import { getChoice, mainUIController } from '@user/client-modules';
|
||||
|
||||
// 写到异步函数里面
|
||||
const selectedDifficulty = await getChoice(
|
||||
mainUIController,
|
||||
[
|
||||
['easy', '新手模式'],
|
||||
['normal', '普通模式'],
|
||||
['hard', '困难模式']
|
||||
],
|
||||
[208, 208, void 0, void 0, 0.5, 0.5], // 居中显示
|
||||
208,
|
||||
{
|
||||
title: '选择难度',
|
||||
titleFont: new Font('黑体', 24)
|
||||
}
|
||||
);
|
||||
|
||||
// 判断选择的内容
|
||||
if (selectedDifficulty === 'hard') {
|
||||
applyHardcoreRules();
|
||||
}
|
||||
```
|
||||
|
||||
##### 分页支持 - 角色选择
|
||||
|
||||
```typescript
|
||||
import { getChoice, mainUIController } from '@user/client-modules';
|
||||
|
||||
// 生成 200 个角色选项
|
||||
const characterOptions = Array.from(
|
||||
{ length: 200 },
|
||||
(_, i) => [i, `角色 #${i + 1}`] as ChoiceItem
|
||||
);
|
||||
|
||||
const chosenId = await getChoice(
|
||||
mainUIController,
|
||||
characterOptions,
|
||||
[208, 208, void 0, void 0, 0.5, 0.5],
|
||||
208,
|
||||
{
|
||||
maxHeight: 400, // 超过 400px 自动分页
|
||||
winskin: 'winskin.png',
|
||||
interval: 12
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
##### 动态样式配置
|
||||
|
||||
```typescript
|
||||
import { getChoice, mainUIController } from '@user/client-modules';
|
||||
|
||||
// 自定义主题风格
|
||||
const choiceResult = await getChoice(
|
||||
mainUIController,
|
||||
[
|
||||
['light', '浅色主题'],
|
||||
['dark', '深色主题'],
|
||||
['oled', 'OLED 深黑']
|
||||
],
|
||||
[208, 208, void 0, void 0, 0.5, 0.5],
|
||||
300,
|
||||
{
|
||||
color: 'rgba(30,30,30,0.9)',
|
||||
border: '#4CAF50',
|
||||
selFill: '#81C784',
|
||||
titleFill: '#FFF59D'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### `waitbox`
|
||||
|
||||
```typescript
|
||||
function waitbox<T>(
|
||||
controller: IUIMountable,
|
||||
loc: ElementLocator,
|
||||
width: number,
|
||||
promise: Promise<T>,
|
||||
props?: Partial<WaitBoxProps<T>>
|
||||
): Promise<T>;
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必填 | 默认值 | 描述 |
|
||||
| ------------ | -------------------------- | ---- | ------ | ---------------------------------------------------- |
|
||||
| `controller` | `IUIMountable` | 是 | - | UI 挂载控制器(通常传递父组件的 `props.controller`) |
|
||||
| `loc` | `ElementLocator` | 是 | - | 定位参数 |
|
||||
| `width` | `number` | 是 | - | 内容区域宽度(像素) |
|
||||
| `promise` | `Promise<T>` | 是 | - | 要监视的异步操作 |
|
||||
| `props` | `Partial<WaitBoxProps<T>>` | 否 | `{}` | 扩展配置项(继承 `Background` + `TextContent` 属性) |
|
||||
|
||||
---
|
||||
|
||||
#### 返回值
|
||||
|
||||
| 类型 | 说明 |
|
||||
| ------------ | ----------------------------------------------------------------------------------- |
|
||||
| `Promise<T>` | 与传入 `Promise` 联动的代理 `Promise`,在以下情况会 `reject`:原始 `Promise` 被拒绝 |
|
||||
|
||||
---
|
||||
|
||||
#### 使用示例
|
||||
|
||||
##### 等待网络请求
|
||||
|
||||
```typescript
|
||||
// 获取用户数据
|
||||
const userData = await waitbox(
|
||||
props.controller,
|
||||
[400, 300, void 0, void 0, 0.5, 0.5], // 居中定位
|
||||
300,
|
||||
fetch('/api/user'),
|
||||
{
|
||||
text: '加载用户信息...',
|
||||
winskin: 'ui/loading_panel'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **控制器有效性**
|
||||
必须确保传入的 `controller` 已正确挂载且未销毁
|
||||
|
||||
2. **异步特性**
|
||||
需使用 `await` 或 `.then()` 处理返回的 Promise
|
||||
|
||||
3. **定位系统**
|
||||
Y 轴坐标基于 Canvas 坐标系(向下为正方向)
|
||||
|
||||
4. **额外参考**
|
||||
- [组件 ConfirmBox](./组件%20ConfirmBox.md)
|
||||
- [组件 Choices](./组件%20Choices.md)
|
||||
- [组件 Waitbox](./组件%20Waitbox.md)
|
37
docs/api/user-client-modules/图标组件.md
Normal file
37
docs/api/user-client-modules/图标组件.md
Normal file
@ -0,0 +1,37 @@
|
||||
# 图标组件 API
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 必填 | 描述 |
|
||||
| ------ | ---------------- | ---- | ---------- |
|
||||
| `loc` | `ElementLocator` | 是 | 图标定位符 |
|
||||
|
||||
图标比例固定,会自动根据传入的长宽缩放。
|
||||
|
||||
## 图标列表
|
||||
|
||||
- `RollbackIcon`: 回退图标
|
||||
- `RetweenIcon`: 回收图标
|
||||
- `ViewMapIcon`: 浏览地图图标
|
||||
- `DanmakuIcon`: 弹幕图标
|
||||
- `ReplayIcon`: 回放图标
|
||||
- `numpadIcon`: 数字键盘图标
|
||||
- `PlayIcon`: 开始播放图标
|
||||
- `PauseIcon`: 暂停播放图标
|
||||
- `DoubleArrow`: 双箭头图标(向右)
|
||||
- `StepForward`: 单步向前图标
|
||||
|
||||
## 使用示例
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { RollbackIcon } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<container>
|
||||
<RollbackIcon loc={[32, 32, 64, 64]} />
|
||||
</container>
|
||||
);
|
||||
});
|
||||
```
|
50
docs/api/user-client-modules/组件 Arrow.md
Normal file
50
docs/api/user-client-modules/组件 Arrow.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Arrow 组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **两点连线**:通过坐标点绘制任意方向箭头
|
||||
- **头部定制**:可调节箭头尖端大小
|
||||
- **样式控制**:支持颜色自定义
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------- | ---------------------------------- | ------ | ----------------------------------------- |
|
||||
| `arrow` | `[number, number, number, number]` | 必填 | 箭头坐标 [起点 x, 起点 y, 终点 x, 终点 y] |
|
||||
| `head` | `number` | `8` | 箭头头部尺寸(像素) |
|
||||
| `color` | `CanvasStyle` | `#fff` | 箭头颜色 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础箭头
|
||||
|
||||
```tsx
|
||||
// 从 (50, 100) 到 (200, 300) 的基础箭头
|
||||
<Arrow arrow={[50, 100, 200, 300]} />
|
||||
```
|
||||
|
||||
### 定制样式
|
||||
|
||||
```tsx
|
||||
// 红色粗箭头(头部尺寸12px)
|
||||
<Arrow arrow={[120, 80, 320, 400]} head={12} color="#FF0000" lineWidth={3} />
|
||||
```
|
||||
|
||||
### 虚线箭头
|
||||
|
||||
```tsx
|
||||
// 带虚线效果的箭头
|
||||
<Arrow
|
||||
arrow={[0, 0, 400, 300]}
|
||||
lineDash={[5, 3]} // 5px实线 + 3px间隔
|
||||
color="gold"
|
||||
/>
|
||||
```
|
78
docs/api/user-client-modules/组件 Background.md
Normal file
78
docs/api/user-client-modules/组件 Background.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Background 背景组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **双样式模式**:支持图片皮肤或纯色填充
|
||||
- **精准定位**:像素级坐标控制
|
||||
- **静态呈现**:无内置动画的稳定背景层
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| --------- | ---------------- | -------- | ----------------------------- |
|
||||
| `loc` | `ElementLocator` | **必填** | 背景定位 |
|
||||
| `winskin` | `ImageIds` | - | 皮肤图片资源 ID(优先级最高) |
|
||||
| `color` | `CanvasStyle` | `"#333"` | 填充颜色(无皮肤时生效) |
|
||||
| `border` | `CanvasStyle` | `"#666"` | 边框颜色(无皮肤时生效) |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 图片皮肤模式
|
||||
|
||||
```tsx
|
||||
// 使用预加载的UI背景图
|
||||
<Background loc={[0, 0, 416, 416]} winskin="winskin.png" />
|
||||
```
|
||||
|
||||
### 纯色模式
|
||||
|
||||
```tsx
|
||||
// 自定义颜色背景
|
||||
<Background
|
||||
loc={[20, 20, 760, 560]}
|
||||
color="gold"
|
||||
border="rgba(255,255,255,0.2)"
|
||||
/>
|
||||
```
|
||||
|
||||
### 对话框组合
|
||||
|
||||
```tsx
|
||||
// 对话框容器
|
||||
<container loc={[200, 100, 400, 300]}>
|
||||
<Background loc={[0, 0, 400, 300]} winskin="winskin.png" />
|
||||
<text loc={[20, 20]} content="系统提示" font={titleFont} />
|
||||
<text loc={[30, 60]} content="确认要离开吗?" font={contentFont} />
|
||||
</container>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **样式优先级**
|
||||
同时指定参数时的生效顺序:
|
||||
|
||||
```tsx
|
||||
// 以下配置仅生效 winskin
|
||||
<Background winskin="bg_wood" color="#FF0000" />
|
||||
```
|
||||
|
||||
2. **默认边框**
|
||||
未指定 border 时的行为:
|
||||
|
||||
```tsx
|
||||
// 无边框(指定为透明色)
|
||||
<Background color="#222" border="transparent" />;
|
||||
|
||||
// 默认白色边框(当未指定任何参数时)
|
||||
<Background />;
|
||||
```
|
159
docs/api/user-client-modules/组件 Choices.md
Normal file
159
docs/api/user-client-modules/组件 Choices.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Choices 选项框组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 组件特性
|
||||
|
||||
- **多选一机制**:从多个选项中选择一项
|
||||
- **自动分页**:通过 `maxHeight` 控制分页
|
||||
- **灵活样式**:支持图片背景/纯色背景 + 自定义字体
|
||||
- **键盘导航**:方向键选择 + Enter 确认
|
||||
- **动态内容**:支持异步加载选项数据
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
ConfirmBoxProps --> TextContentProps
|
||||
|
||||
click TextContentProps "./组件%20TextContent"
|
||||
```
|
||||
|
||||
本组件完全继承 `TextContent` 组件的参数,参考 [组件 TextContent](./组件%20TextContent.md)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ----------- | ---------------- | -------- | ------------------------------------- |
|
||||
| `choices` | `ChoiceItem[]` | 必填 | 选项数组,格式为 `[key, text]` 的元组 |
|
||||
| `loc` | `ElementLocator` | 必填 | 定位配置(需包含 x,y 坐标及锚点) |
|
||||
| `width` | `number` | 必填 | 选项框宽度(像素) |
|
||||
| `maxHeight` | `number` | `360` | 最大高度(超过时自动分页) |
|
||||
| `text` | `string` | - | 主说明文本(显示在标题下方) |
|
||||
| `title` | `string` | - | 标题文本 |
|
||||
| `winskin` | `ImageIds` | - | 背景图片资源 ID(与 color 互斥) |
|
||||
| `color` | `CanvasStyle` | `#333` | 背景颜色(未设置 winskin 时生效) |
|
||||
| `border` | `CanvasStyle` | `gold` | 边框颜色/样式 |
|
||||
| `selFont` | `Font` | 系统默认 | 选项文本字体 |
|
||||
| `selFill` | `CanvasStyle` | `#fff` | 选项文本颜色 |
|
||||
| `titleFont` | `Font` | 系统默认 | 标题字体 |
|
||||
| `titleFill` | `CanvasStyle` | `gold` | 标题颜色 |
|
||||
| `interval` | `number` | `16` | 选项间垂直间距(像素) |
|
||||
|
||||
---
|
||||
|
||||
## Events 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| -------- | ---------------- | ---------------------- |
|
||||
| `choose` | `key: ChoiceKey` | 用户选择任意选项时触发 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法 - 系统设置
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Choices, ChoiceItem } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const options: ChoiceItem[] = [
|
||||
['low', '低画质'],
|
||||
['medium', '中画质'],
|
||||
['high', '高画质']
|
||||
];
|
||||
|
||||
return () => (
|
||||
<Choices
|
||||
choices={options}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
width={208}
|
||||
title="图形质量设置"
|
||||
text="请选择适合您设备的画质等级"
|
||||
// key 在这里是每个选项的第一个元素,即 low, medium, high
|
||||
onChoose={key => console.log(`Choose ${key}.`)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 分页处理 - 角色选择
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Choices, ChoiceItem } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
// 生成 50 个角色选项
|
||||
const characters: ChoiceItem[] = Array.from(
|
||||
{ length: 50 },
|
||||
(_, i) => [`char_${i}`, `角色 ${i + 1}`] as ChoiceItem
|
||||
);
|
||||
|
||||
return () => (
|
||||
<Choices
|
||||
choices={characters}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
width={208}
|
||||
maxHeight={400} // 高度超过 400px 自动分页
|
||||
interval={12}
|
||||
winskin="winskin.png"
|
||||
// 粗体,20px 大小的 Verdana 字体
|
||||
titleFont={new Font('Verdana', 20, 'px', 700)}
|
||||
selFill="#4CAF50"
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态内容 + 自定义样式
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Choices, ChoiceItem } from '@user/client-modules';
|
||||
import { onTick } from '@motajs/render';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const dynamicOptions = ref<ChoiceItem[]>([]);
|
||||
|
||||
onTick(() => {
|
||||
// 每帧生成随机选项名称
|
||||
dynamicOptions.value = Array.from(
|
||||
{ length: 50 },
|
||||
(_, i) =>
|
||||
[
|
||||
`char_${i}`,
|
||||
`随机数 ${Math.random().toFixed(8)}`
|
||||
] as ChoiceItem
|
||||
);
|
||||
});
|
||||
|
||||
return () => (
|
||||
<Choices
|
||||
choices={dynamicOptions.value}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
width={208}
|
||||
color="rgba(30,30,30,0.9)"
|
||||
border="#607D8B"
|
||||
title="选择随机数"
|
||||
titleFill="#B2EBF2"
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **选项键值唯一性**
|
||||
每个选项的 `key` 必须唯一,否则可能引发不可预期行为
|
||||
|
||||
2. **分页计算规则**
|
||||
分页依据 `maxHeight` 和字体大小自动计算,需确保字体大小一致
|
||||
|
||||
3. **使用更方便的函数**:多数情况下,你不需要使用本组件,使用包装好的函数往往会更加方便,参考 [`getChoice`](./functions.md#getchoice)
|
124
docs/api/user-client-modules/组件 ConfirmBox.md
Normal file
124
docs/api/user-client-modules/组件 ConfirmBox.md
Normal file
@ -0,0 +1,124 @@
|
||||
# ConfirmBox 确认框组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 组件特性
|
||||
|
||||
- **双选项支持**:是/否选择
|
||||
- **灵活样式**:支持图片背景或纯色背景
|
||||
- **键盘交互**:支持按键操作
|
||||
- **自动布局**:根据内容动态计算高度
|
||||
- **事件驱动**:提供明确的用户选择反馈
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
ConfirmBoxProps --> TextContentProps
|
||||
|
||||
click TextContentProps "./组件%20TextContent"
|
||||
```
|
||||
|
||||
本组件完全继承 `TextContent` 组件的参数,参考 [组件 TextContent](./组件%20TextContent.md)
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------------ | ---------------- | ------------ | --------------------------------- |
|
||||
| `text` | `string` | 必填 | 显示的主文本内容 |
|
||||
| `width` | `number` | 必填 | 确认框宽度(像素) |
|
||||
| `loc` | `ElementLocator` | 必填 | 定位配置 |
|
||||
| `winskin` | `ImageIds` | - | 背景图片资源 ID(与 color 互斥) |
|
||||
| `color` | `CanvasStyle` | `'#333'` | 背景颜色(未设置 winskin 时生效) |
|
||||
| `border` | `CanvasStyle` | `'gold'` | 边框颜色/样式 |
|
||||
| `selFont` | `Font` | 系统默认字体 | 选项按钮字体 |
|
||||
| `selFill` | `CanvasStyle` | `'#d48'` | 选项按钮文本颜色 |
|
||||
| `yesText` | `string` | `'是'` | 确认按钮文本 |
|
||||
| `noText` | `string` | `'否'` | 取消按钮文本 |
|
||||
| `defaultYes` | `boolean` | `true` | 默认选中确认按钮 |
|
||||
|
||||
---
|
||||
|
||||
## Events 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| ------ | ---- | --------------------------- |
|
||||
| `yes` | - | 用户选择确认时触发 |
|
||||
| `no` | - | 用户选择取消或按 Esc 时触发 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法 - 文本确认
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { ConfirmBox } from '@user/client-modules';
|
||||
|
||||
export const MyCom defineComponent(() => {
|
||||
return () => (
|
||||
<ConfirmBox
|
||||
text="确定要保存当前进度吗?"
|
||||
width={208}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
onYes={() => console.log('用户确认保存')}
|
||||
onNo={() => console.log('用户取消保存')}
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 图片背景 + 自定义按钮
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { ConfirmBox } from '@user/client-modules';
|
||||
import { Font } from '@motajs/render';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<ConfirmBox
|
||||
text="此操作不可逆,是否继续?"
|
||||
width={208}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
// 背景使用 winskin
|
||||
winskin="winskin.png"
|
||||
yesText="确认删除"
|
||||
noText="取消操作"
|
||||
// 设置选项字体
|
||||
selFont={new Font('Verdana')}
|
||||
selFill="#f44"
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态内容 + 编程控制
|
||||
|
||||
```tsx
|
||||
import { defineComponent, computed } from 'vue';
|
||||
import { ConfirmBox } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const count = ref(0);
|
||||
const myText = computed(() => `当前确认次数与取消次数差值:${count.value}`);
|
||||
|
||||
return () => (
|
||||
<ConfirmBox
|
||||
text={myText.value}
|
||||
width={360}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]}
|
||||
color="rgba(0,0,0,0.8)"
|
||||
border="#4CAF50"
|
||||
defaultYes={false}
|
||||
onYes={() => void count.value++} // 每次确认次数加一
|
||||
onNo={() => void count.value--} // 每次确认次数减一
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **使用更方便的函数**:多数情况下,你不需要使用本组件,使用包装好的函数往往会更加方便,参考 [`getConfirm`](./functions.md#getconfirm)
|
192
docs/api/user-client-modules/组件 Page.md
Normal file
192
docs/api/user-client-modules/组件 Page.md
Normal file
@ -0,0 +1,192 @@
|
||||
# Page 分页组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 组件描述
|
||||
|
||||
分页组件用于将大量内容分割为多个独立页面展示,支持通过编程控制或用户交互进行页面切换。适用于存档界面、多步骤表单等场景。
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| -------------- | ---------------- | ----------------- | -------------------------------- |
|
||||
| `pages` | `number` | 必填 | 总页数 |
|
||||
| `loc` | `ElementLocator` | 必填 | 页码组件定位配置(坐标系及位置) |
|
||||
| `font` | `Font` | `Font.defaults()` | 页码文本字体配置(可选) |
|
||||
| `hideIfSingle` | `boolean` | `false` | 当总页数为 1 时是否隐藏页码组件 |
|
||||
|
||||
---
|
||||
|
||||
## Events 事件说明
|
||||
|
||||
| 事件名 | 参数类型 | 触发时机 |
|
||||
| ------------ | ---------------- | ------------------------------- |
|
||||
| `pageChange` | `(page: number)` | 当前页码变化时触发(从 0 开始) |
|
||||
|
||||
---
|
||||
|
||||
## Slots 插槽说明
|
||||
|
||||
### `default`
|
||||
|
||||
接收当前页码(从 0 开始)并返回需要渲染的内容
|
||||
**参数**
|
||||
|
||||
- `page: number` 当前页码索引(0-based)
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
| ------------ | --------------- | -------- | --------------------------------------------------- |
|
||||
| `changePage` | `page: number` | `void` | 跳转到指定页码(0-based,自动约束在有效范围内) |
|
||||
| `movePage` | `delta: number` | `void` | 基于当前页码进行偏移切换(如 +1 下一页,-1 上一页) |
|
||||
| `now` | - | `number` | 获取当前页码索引(0-based) |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法 - 多页文本展示
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Page, PageExpose } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<Page pages={3} loc={[208, 208, 208, 208, 0.5, 0.5]}>
|
||||
{page => (
|
||||
<text
|
||||
text={`第 ${page + 1} 页内容`}
|
||||
loc={[104, 104, void 0, void 0, 0.5, 0.5]}
|
||||
/>
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 监听页面修改
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { Page, PageExpose } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
// 示例数据
|
||||
const pages = [
|
||||
{ content: '第一页内容' },
|
||||
{ content: '第二页内容' },
|
||||
{ content: '第三页内容' }
|
||||
];
|
||||
|
||||
// 分页组件引用
|
||||
const pageRef = ref<PageExpose>();
|
||||
|
||||
// 页码变化回调
|
||||
const handlePageChange = (currentPage: number) => {
|
||||
// 可以使用参数获得当前页码,加一是因为页码是从 0 开始的
|
||||
console.log(`当前页码:${currentPage + 1}`);
|
||||
// 或者也可以使用 Page 组件的接口获得当前页码
|
||||
console.log(`当前页码:${pageRef.value!.now() + 1}`);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<Page
|
||||
pages={pages.length}
|
||||
loc={[208, 208, 208, 208, 0.5, 0.5]} // 游戏画面居中
|
||||
onPageChange={handlePageChange}
|
||||
ref={pageRef}
|
||||
>
|
||||
{page => <text text={pages[page].content} />}
|
||||
</Page>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态配置示例
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { Page, PageExpose } from '@user/client-modules';
|
||||
|
||||
// 带统计面板的复杂分页
|
||||
export const MyCom = defineComponent(() => {
|
||||
const dataPages = [
|
||||
/* 复杂数据结构 */
|
||||
];
|
||||
|
||||
// 暴露方法实现翻页逻辑
|
||||
const pageRef = ref<PageExpose>();
|
||||
const jumpToAnalysis = () => pageRef.value?.changePage(3); // 1-based
|
||||
|
||||
return () => (
|
||||
<container>
|
||||
{/* 分页内容 */}
|
||||
<Page
|
||||
pages={dataPages.length}
|
||||
loc={[208, 208, 208, 208, 0.5, 0.5]}
|
||||
hideIfSingle // 如果只有一个页面,那么隐藏底部的页码显示
|
||||
ref={pageRef}
|
||||
>
|
||||
{page => (
|
||||
<container>
|
||||
{/* 这里面可以写一些复杂的渲染内容,或者单独写成一个组件,把页码作为参数传入 */}
|
||||
<text text="渲染内容" />
|
||||
<g-rect loc={[50, 50, 100, 50]} stroke />
|
||||
</container>
|
||||
)}
|
||||
</Page>
|
||||
|
||||
{/* 自定义跳转按钮 */}
|
||||
<text
|
||||
loc={[108, 180, void 0, void 0, 0.5, 1]} // 左右居中,靠下对齐
|
||||
onClick={jumpToAnalysis}
|
||||
text="跳转到分析页"
|
||||
/>
|
||||
</container>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 边缘检测示例
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
// 边界处理逻辑
|
||||
export const MyCom = defineComponent(() => {
|
||||
const pageRef = ref<PageExpose>();
|
||||
|
||||
// 自定义边界提示
|
||||
const handleEdge = () => {
|
||||
const current = pageRef.value?.now() ?? 0;
|
||||
const total = pageRef.value?.pages ?? 0;
|
||||
|
||||
// 到达边界时提示(可以换成其他提示方式)
|
||||
if (current === 0) core.drawTip('已经是第一页');
|
||||
if (current === total - 1) core.drawTip('已经是最后一页');
|
||||
};
|
||||
|
||||
return () => (
|
||||
<Page
|
||||
pages={8}
|
||||
ref={pageRef}
|
||||
onPageChange={handleEdge}
|
||||
loc={[208, 208, 208, 208, 0.5, 0.5]}
|
||||
>
|
||||
{page => <text text={`第${page}页`} />}
|
||||
</Page>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **自动约束**:切换页码时会自动约束在 `[0, pages-1]` 范围内
|
107
docs/api/user-client-modules/组件 Progress.md
Normal file
107
docs/api/user-client-modules/组件 Progress.md
Normal file
@ -0,0 +1,107 @@
|
||||
# Progress 组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **动态进度显示**:支持 0.0~1.0 范围的进度可视化
|
||||
- **双色样式分离**:可分别定制已完成/未完成部分样式
|
||||
- **精准定位**:支持像素级坐标控制
|
||||
- **平滑过渡**:数值变化自动触发重绘
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------------ | ---------------- | -------- | --------------------- |
|
||||
| `loc` | `ElementLocator` | **必填** | 进度条容器坐标 |
|
||||
| `progress` | `number` | **必填** | 当前进度值(0.0~1.0) |
|
||||
| `success` | `CanvasStyle` | `green` | 已完成部分填充样式 |
|
||||
| `background` | `CanvasStyle` | `gray` | 未完成部分填充样式 |
|
||||
| `lineWidth` | `number` | `2` | 进度条线宽(像素) |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { Progress } from '@user/client-modules';
|
||||
import { onTick } from '@motajs/render';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
// 创建响应式进度值
|
||||
const loadingProgress = ref(0);
|
||||
|
||||
// 模拟进度更新
|
||||
onTick(() => {
|
||||
if (loadingProgress.value < 1) {
|
||||
loadingProgress.value += 0.002;
|
||||
}
|
||||
});
|
||||
|
||||
return () => (
|
||||
<Progress
|
||||
loc={[20, 20, 200, 8]} // x=20, y=20, 宽200px, 高8px
|
||||
progress={loadingProgress.value}
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 自定义样式
|
||||
|
||||
```tsx
|
||||
// 自定义进度条的已完成和未完成部分的样式
|
||||
<Progress
|
||||
loc={[50, 100, 300, 12]}
|
||||
progress={0.65}
|
||||
success="rgb(54, 255, 201)"
|
||||
background="rgba(255,255,255,0.2)"
|
||||
lineWidth={6}
|
||||
/>
|
||||
```
|
||||
|
||||
### 垂直进度条
|
||||
|
||||
```tsx
|
||||
// 通过旋转容器实现垂直效果,注意锚点的使用
|
||||
<container rotation={-Math.PI / 2} loc={[100, 200, 150, 8, 0.5, 0.5]}>
|
||||
<Progress loc={[0, 0, 150, 8]} progress={0.8} success="#FF5722" />
|
||||
</container>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 动画效果实现
|
||||
|
||||
### 平滑过渡示例
|
||||
|
||||
```tsx
|
||||
import { transitioned } from '@user/client-modules';
|
||||
import { pow } from 'mutate-animate';
|
||||
|
||||
const progressValue = transitioned(0, 2000, pow(2, 'out'));
|
||||
progressValue.set(1); // 2秒内完成二次曲线平滑过渡
|
||||
|
||||
return () => (
|
||||
<Progress loc={[0, 0, 400, 10]} progress={progressValue.ref.value} />
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **坐标系统**
|
||||
实际渲染高度由 `loc[3]` 参数控制,会自动上下居中:
|
||||
|
||||
```tsx
|
||||
// 情况1:显式指定高度为8px
|
||||
<Progress loc={[0, 0, 200, 8]} />
|
||||
```
|
118
docs/api/user-client-modules/组件 Scroll.md
Normal file
118
docs/api/user-client-modules/组件 Scroll.md
Normal file
@ -0,0 +1,118 @@
|
||||
# Scroll 滚动组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 组件特性
|
||||
|
||||
- **虚拟滚动**:自动裁剪可视区域外元素
|
||||
- **双模式支持**:垂直/水平滚动(默认垂直)
|
||||
- **性能优化**:动态计算可视区域,支持万级元素流畅滚动
|
||||
- **编程控制**:支持精准定位滚动位置
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ---------- | ---------------- | ------- | ------------------------------------------------- |
|
||||
| `hor` | `boolean` | `false` | 启用水平滚动模式 |
|
||||
| `noscroll` | `boolean` | `false` | 是否不显示滚动条,可用于一些特殊场景 |
|
||||
| `loc` | `ElementLocator` | 必填 | 滚动容器定位配置 |
|
||||
| `padEnd` | `number` | `0` | 滚动到底部/右侧的额外留白(用于修正自动计算误差) |
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
| ----------------- | --------------------------------- | -------- | --------------------------------------------------- |
|
||||
| `scrollTo` | `position: number, time?: number` | `void` | 滚动到指定位置(单位:像素),time 为过渡时间(ms) |
|
||||
| `getScrollLength` | - | `number` | 获取最大可滚动距离(单位:像素) |
|
||||
|
||||
---
|
||||
|
||||
## Slots 插槽说明
|
||||
|
||||
### `default`
|
||||
|
||||
接收滚动内容,**必须直接包含可渲染元素**
|
||||
⚠️ 禁止嵌套 container 包裹,推荐平铺结构:
|
||||
|
||||
```tsx
|
||||
// ✅ 正确写法
|
||||
<Scroll>
|
||||
<item />
|
||||
<item />
|
||||
<item />
|
||||
</Scroll>;
|
||||
|
||||
// ❌ 错误写法(影响虚拟滚动计算)
|
||||
<Scroll>
|
||||
<container>
|
||||
// 会导致整体被视为单个元素
|
||||
<item />
|
||||
<item />
|
||||
</container>
|
||||
</Scroll>;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础垂直滚动
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const list = Array(200).fill(0);
|
||||
|
||||
return () => (
|
||||
<Scroll loc={[208, 208, 208, 208, 0.5, 0.5]}>
|
||||
{list.map((_, index) => (
|
||||
<text key={index} text={index.toString()} />
|
||||
))}
|
||||
</Scroll>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 水平滚动 + 编程控制
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref, onMounted } from 'vue';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const list = Array(200).fill(0);
|
||||
const scrollRef = ref<ScrollExpose>();
|
||||
|
||||
// 滚动水平 100 像素位置,动画时长 500 毫秒
|
||||
onMounted(() => {
|
||||
scrollRef.value?.scrollTo(100, 500);
|
||||
});
|
||||
|
||||
return () => (
|
||||
<Scroll hor loc={[208, 208, 208, 208, 0.5, 0.5]} ref={scrollRef}>
|
||||
{list.map((_, index) => (
|
||||
<text key={index} text={index.toString()} />
|
||||
))}
|
||||
</Scroll>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能优化指南
|
||||
|
||||
### 1. 替代方案建议
|
||||
|
||||
⚠️ **当子元素数量 > 1000 时**,推荐改用分页组件:
|
||||
|
||||
```tsx
|
||||
// 使用 Page 组件处理超大数据集
|
||||
<Page pages={Math.ceil(data.length / 50)}>
|
||||
{page => renderChunk(data.slice(page * 50, (page + 1) * 50))}
|
||||
</Page>
|
||||
```
|
160
docs/api/user-client-modules/组件 ScrollText.md
Normal file
160
docs/api/user-client-modules/组件 ScrollText.md
Normal file
@ -0,0 +1,160 @@
|
||||
# ScrollText 组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **自动滚动**:支持设定滚动速度的纵向滚动效果
|
||||
- **长文本支持**:内置高性能文本渲染引擎
|
||||
- **精准控制**:提供播放/暂停/调速等操作接口
|
||||
- **智能布局**:自动计算文本高度和滚动距离
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
ScrollTextProps --> TextContentProps
|
||||
ScrollTextProps --> ScrollProps
|
||||
|
||||
click TextContentProps "./组件%20TextContent"
|
||||
click ScrollProps "./组件%20Scroll"
|
||||
```
|
||||
|
||||
完全继承 `TextContent` 组件和 `Scroll` 组件的参数和事件。
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------- | ---------------- | ------ | --------------------------- |
|
||||
| `speed` | `number` | 必填 | 滚动速度(像素/秒) |
|
||||
| `width` | `number` | 必填 | 文本区域固定宽度(像素) |
|
||||
| `loc` | `ElementLocator` | 必填 | 容器定位 [x,y,width,height] |
|
||||
| `pad` | `number` | `16` | 首行前空白距离(像素) |
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| ----------- | ---- | -------------------- |
|
||||
| `scrollEnd` | - | 滚动到文本末尾时触发 |
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
| ---------- | --------------- | ------ | --------------------------- |
|
||||
| `pause` | - | void | 暂停滚动 |
|
||||
| `resume` | - | void | 继续滚动 |
|
||||
| `setSpeed` | `speed: number` | void | 动态调整滚动速度(像素/秒) |
|
||||
| `rescroll` | - | void | 立即重置到起始位置重新滚动 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础滚动
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { ScrollText } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const longText = '序幕\n'.repeat(100) + '——全剧终——';
|
||||
|
||||
return () => (
|
||||
<ScrollText
|
||||
text={longText}
|
||||
speed={80} // 每秒滚动80像素
|
||||
width={416} // 文本区域宽度
|
||||
loc={[0, 0, 416, 416]} // 容器位置和尺寸
|
||||
fillStyle="#E6E6FA" // 薰衣草色文字
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态控制
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { ScrollText } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const longText = '序幕\n'.repeat(100) + '——全剧终——';
|
||||
const scrollRef = ref<ScrollTextExpose>();
|
||||
|
||||
// 暂停/恢复控制
|
||||
const toggleScroll = () => {
|
||||
if (scrollRef.value?.isPaused) {
|
||||
scrollRef.value.resume();
|
||||
} else {
|
||||
scrollRef.value?.pause();
|
||||
}
|
||||
};
|
||||
|
||||
// 速度控制
|
||||
const accelerate = () => {
|
||||
scrollRef.value?.setSpeed(200);
|
||||
};
|
||||
|
||||
return () => (
|
||||
<ScrollText
|
||||
ref={scrollRef}
|
||||
text={longText}
|
||||
speed={100}
|
||||
width={416}
|
||||
loc={[0, 0, 416, 416]}
|
||||
onScrollEnd={() => console.log('滚动结束')}
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 复杂排版
|
||||
|
||||
```tsx
|
||||
const staffText =
|
||||
'\\c[32]====制作人员====\\c\n\n' +
|
||||
'\\r[#FFD700]总监督\\r\t\t张三\n' +
|
||||
'\\r[#00FF00]美术指导\\r\\t李四\n' +
|
||||
'\\i[logo]\n' +
|
||||
'特别感谢:某某公司';
|
||||
|
||||
<ScrollText
|
||||
text={staffText}
|
||||
speed={120}
|
||||
width={720}
|
||||
loc={[40, 40, 720, 560]}
|
||||
pad={40} // 顶部留白
|
||||
font={new Font('黑体', 24)}
|
||||
lineHeight={8} // 行间距
|
||||
interval={0} // 禁用打字机效果
|
||||
/>;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **容器尺寸**
|
||||
实际可滚动区域计算公式:
|
||||
|
||||
```
|
||||
可视高度 = loc[3](容器高度)
|
||||
滚动距离 = 文本总高度 + pad(首行前空白)
|
||||
```
|
||||
|
||||
2. **速度控制**
|
||||
推荐速度范围 50-200 像素/秒
|
||||
|
||||
3. **组合动画**
|
||||
可与容器变换配合实现复杂效果:
|
||||
```tsx
|
||||
<container rotation={-5} alpha={0.9}>
|
||||
<ScrollText />
|
||||
</container>
|
||||
```
|
77
docs/api/user-client-modules/组件 Selection.md
Normal file
77
docs/api/user-client-modules/组件 Selection.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Selection 组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **动态高亮**:自动呼吸动画效果
|
||||
- **双样式模式**:支持图片皮肤或纯色样式
|
||||
- **精准定位**:像素级坐标控制
|
||||
- **透明度动画**:可定制不透明度变化范围
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ------------ | ------------------ | -------------- | --------------------------------- |
|
||||
| `loc` | `ElementLocator` | **必填** | 光标定位 |
|
||||
| `winskin` | `ImageIds` | - | 图片资源 ID(优先级最高) |
|
||||
| `color` | `CanvasStyle` | `#ddd` | 填充颜色(无皮肤时生效) |
|
||||
| `border` | `CanvasStyle` | `gold` | 边框颜色(无皮肤时生效) |
|
||||
| `alphaRange` | `[number, number]` | `[0.25, 0.55]` | 不透明度波动范围 [最小值, 最大值] |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 图片皮肤模式
|
||||
|
||||
```tsx
|
||||
// 使用预加载的游戏皮肤资源
|
||||
<Selection
|
||||
loc={[120, 80, 160, 24]}
|
||||
winskin="winskin.png"
|
||||
alphaRange={[0.4, 1.0]}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 纯色模式
|
||||
|
||||
```tsx
|
||||
// 自定义颜色方案
|
||||
<Selection
|
||||
loc={[20, 240, 200, 32]}
|
||||
color="rgba(0,128,255,0.2)" // 填充颜色
|
||||
border="#00BFFF" // 边框颜色
|
||||
alphaRange={[0.5, 0.9]}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **样式优先级**
|
||||
同时指定 `winskin` 和颜色参数时:
|
||||
|
||||
```tsx
|
||||
// 以下配置将忽略 color/border 参数
|
||||
<Selection winskin="winskin.png" color="red" border="blue" />
|
||||
```
|
||||
|
||||
2. **动画速度**
|
||||
呼吸动画固定为 2000ms/周期,暂不支持自定义时长
|
||||
|
||||
3. **点击反馈**
|
||||
建议配合事件系统实现点击效果:
|
||||
```tsx
|
||||
<container onClick={handleClick}>
|
||||
<Selection />
|
||||
<text />
|
||||
</container>
|
||||
```
|
184
docs/api/user-client-modules/组件 TextContent.md
Normal file
184
docs/api/user-client-modules/组件 TextContent.md
Normal file
@ -0,0 +1,184 @@
|
||||
# TextContent 文本组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **自动布局**:根据宽度自动换行
|
||||
- **样式控制**:支持动态修改字体/颜色/描边
|
||||
- **打字机效果**:逐字显示支持
|
||||
- **动态高度**:自适应或固定高度模式
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| ----------------- | ------------- | ----------------- | ------------------------------ |
|
||||
| `text` | `string` | - | 显示文本(支持转义字符) |
|
||||
| `width` | `number` | 必填 | 文本区域宽度(像素) |
|
||||
| `fill` | `boolean` | `true` | 是否对文字填充 |
|
||||
| `stroke` | `boolean` | `false` | 是否对文字描边 |
|
||||
| `font` | `Font` | 系统默认字体 | 字体配置对象 |
|
||||
| `lineHeight` | `number` | `0` | 行间距(像素) |
|
||||
| `interval` | `number` | `50` | 打字机字符间隔(ms) |
|
||||
| `autoHeight` | `boolean` | `false` | 是否根据内容自动调整高度 |
|
||||
| `fillStyle` | `CanvasStyle` | `#fff` | 文字填充颜色 |
|
||||
| `strokeStyle` | `CanvasStyle` | `#000` | 文字描边颜色 |
|
||||
| `strokeWidth` | `number` | `1` | 描边宽度 |
|
||||
| `textAlign` | `TextAlign` | `TextAlign.Left` | 文字对齐方式 |
|
||||
| `wordBreak` | `WordBreak` | `WordBreak.Space` | 文本分词原则,将会影响换行表现 |
|
||||
| `breakChars` | `string` | - | 会被分词规则识别的分词字符 |
|
||||
| `ignoreLineStart` | `string` | - | 不允许出现在行首的字符 |
|
||||
| `ignoreLineEnd` | `string` | - | 不允许出现在行尾的字符 |
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| -------------- | ---------------- | ------------------ |
|
||||
| `typeStart` | - | 开始逐字显示时 |
|
||||
| `typeEnd` | - | 全部文字显示完成时 |
|
||||
| `updateHeight` | `height: number` | 文本高度变化时 |
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
| ----------- | ---- | -------- | ---------------------------- |
|
||||
| `retype` | - | `void` | 从头开始重新打字 |
|
||||
| `showAll` | - | `void` | 立刻结束打字机,显示所有文字 |
|
||||
| `getHeight` | - | `number` | 获得当前文本的高度 |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础用法 - 对话文本
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { TextContent } from '@user/client-modules';
|
||||
import { Font } from '@motajs/render';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<TextContent
|
||||
text="这是基础文本内容,会自动换行排版"
|
||||
width={200} // 最大宽度,达到这个宽度会自动换行
|
||||
font={new Font('宋体', 18)}
|
||||
fillStyle="#333333" // 黑色字体
|
||||
onTypeEnd={() => console.log('显示完成')} // 打字机结束后执行
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 自定义样式 + 描边效果
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { TextContent } from '@user/client-modules';
|
||||
import { Font } from '@motajs/render';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<TextContent
|
||||
text="\\r[#FFD700]金色描边文字"
|
||||
width={300}
|
||||
font={new Font('黑体', 24)}
|
||||
stroke // 设为填充+描边格式,如果仅描边需要设置 fill={false}
|
||||
strokeStyle="#8B4513" // 描边颜色
|
||||
strokeWidth={2} // 描边宽度
|
||||
lineHeight={8} // 行间距,8 像素
|
||||
interval={30} // 打字机间隔
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态内容更新
|
||||
|
||||
```tsx
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { TextContent } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const dynamicText = ref('初始内容');
|
||||
|
||||
setTimeout(() => {
|
||||
dynamicText.value = '更新后的内容\\z[5]带暂停效果';
|
||||
}, 2000);
|
||||
|
||||
return () => (
|
||||
<TextContent
|
||||
text={dynamicText.value}
|
||||
width={500}
|
||||
autoHeight
|
||||
onUpdateHeight={h => console.log('当前高度:', h)} // 当高度发生变化时触发
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 禁用动画效果
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { TextContent } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<TextContent
|
||||
text="立即显示所有内容"
|
||||
width={350}
|
||||
interval={0} // 设置为0禁用逐字效果
|
||||
showAll // 立即显示全部
|
||||
fillStyle="rgba(0,128,0,0.8)"
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 多语言复杂排版
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { TextContent } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const complexText =
|
||||
'\\g[Times New Roman]Hello\\g[宋体] 你好 \\i[flag]\\n' +
|
||||
'\\r[#FF5733]Multi\\r[#3498db]-\\r[#2ECC71]Color\\r\\n' +
|
||||
'\\c[18]Small\\c[24]Size\\c[30]Changes';
|
||||
|
||||
return () => (
|
||||
<TextContent
|
||||
text={complexText}
|
||||
width={600}
|
||||
interval={25}
|
||||
onTypeStart={() => console.log('开始渲染复杂文本')}
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 转义字符示例
|
||||
|
||||
```tsx
|
||||
// 颜色/字体/图标综合使用
|
||||
const styledText =
|
||||
'\\r[#FF0000]警告!\\g[方正粗宋]\\c[24]' +
|
||||
'\\i[warning_icon]发现异常\\z[10]\\n' +
|
||||
'请立即处理\\r\\g\\c';
|
||||
|
||||
<TextContent text={styledText} width={450} interval={40} />;
|
||||
```
|
||||
|
||||
转义字符具体用法参考 [TextContentParser](./TextContentParser.md#转义字符语法说明)
|
173
docs/api/user-client-modules/组件 Textbox.md
Normal file
173
docs/api/user-client-modules/组件 Textbox.md
Normal file
@ -0,0 +1,173 @@
|
||||
# Textbox 对话框组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
本文档描述在 `TextContent` 基础上扩展的对话框组件,专为剧情对话、系统提示等场景设计,支持背景、标题等装饰元素。
|
||||
|
||||
---
|
||||
|
||||
## 特有属性说明
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TextboxProps --> TextContentProps
|
||||
|
||||
click TextContentProps "./组件%20TextContent"
|
||||
```
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 描述 |
|
||||
| -------------- | ------------- | -------------- | ------------------------------------- |
|
||||
| `backColor` | `CanvasStyle` | `#222` | 背景颜色(与 `winskin` 互斥) |
|
||||
| `winskin` | `ImageIds` | - | 背景图片资源 ID(优先于 `backColor`) |
|
||||
| `padding` | `number` | `8` | 内容区域与边框的内边距(像素) |
|
||||
| `title` | `string` | - | 标题文本内容 |
|
||||
| `titleFont` | `Font` | `18px Verdana` | 标题字体配置 |
|
||||
| `titleFill` | `CanvasStyle` | `gold` | 标题文字颜色 |
|
||||
| `titleStroke` | `CanvasStyle` | `transparent` | 标题描边颜色 |
|
||||
| `titlePadding` | `number` | `8` | 标题在其背景的间距(像素) |
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
TextboxEmits --> TextContentEmits
|
||||
|
||||
click TextContentEmits "./组件%20TextContent"
|
||||
```
|
||||
|
||||
完全继承 `TextContent` 的事件。
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 描述 |
|
||||
| --------- | ---- | ------ | ---------------------------- |
|
||||
| `retype` | - | `void` | 从头开始重新打字 |
|
||||
| `showAll` | - | `void` | 立刻结束打字机,显示所有文字 |
|
||||
| `show` | - | `void` | 显示这个文本框 |
|
||||
| `hide` | - | `void` | 隐藏这个文本框 |
|
||||
|
||||
---
|
||||
|
||||
## Slots 插槽说明
|
||||
|
||||
### default
|
||||
|
||||
背景插槽,传入后可以自定义背景
|
||||
|
||||
```tsx
|
||||
// 例如使用一张图片作为背景
|
||||
<Textbox>
|
||||
<container>
|
||||
<image image="myImage.png" />
|
||||
</container>
|
||||
</Textbox>
|
||||
```
|
||||
|
||||
### title
|
||||
|
||||
标题背景插槽,自定义标题背景
|
||||
|
||||
```tsx
|
||||
// 与 default 一起使用
|
||||
<Textbox>
|
||||
{{
|
||||
// 背景图
|
||||
default: () => <image image="myImage.png" />,
|
||||
// 标题背景图
|
||||
title: () => <g-rectr circle={[4]} fill />
|
||||
}}
|
||||
</Textbox>
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础对话框
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Textbox } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<Textbox
|
||||
title="NPC对话"
|
||||
winskin="winskin.png" // 背景 winskin
|
||||
padding={12} // 文字内边距
|
||||
width={416} // 最大宽度,超过换行,自动减去内边距
|
||||
titleFont={new Font('楷体', 22)} // 标题字体
|
||||
titleFill="#FFD700" // 标题填充样式
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 纯色背景 + 复杂标题
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Textbox } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<Textbox
|
||||
title="国王的旨意" // 标题内容
|
||||
backColor="rgba(0,0,0,0.8)" // 背景色,使用半透明黑色
|
||||
padding={16}
|
||||
width={416}
|
||||
titlePadding={10} // 标题的内边距
|
||||
titleFont={new Font('华文行楷', 24)}
|
||||
titleStroke="#8B4513" // 标题描边样式
|
||||
titleFill="#FFFFFF"
|
||||
/>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 动态标题交互
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Textbox, TextbosExpose } from '@user/client-modules';
|
||||
|
||||
export const MyCom = defineComponent(() => {
|
||||
const currentTitle = ref<string>();
|
||||
|
||||
// 点击按钮切换标题
|
||||
const toggleTitle = () => {
|
||||
currentTitle.value += 1;
|
||||
};
|
||||
|
||||
return () => (
|
||||
<container>
|
||||
<Textbox
|
||||
ref={dialogRef}
|
||||
title={currentTitle.value.toString()}
|
||||
winskin="winskin.png"
|
||||
padding={10}
|
||||
width={416}
|
||||
titleFill="#FF4444"
|
||||
/>
|
||||
<text text="点击切换标题" onClick={toggleTitle} />
|
||||
</container>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 布局结构示意图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Dialog[对话框] --> Background[背景层]
|
||||
Dialog --> Title[标题层]
|
||||
Dialog --> Content[内容层]
|
||||
|
||||
Background -->|winskin/backColor| 渲染背景
|
||||
Title -->|title 配置| 标题文本
|
||||
Content -->|padding 控制| 文字内容
|
||||
```
|
65
docs/api/user-client-modules/组件 Tip.md
Normal file
65
docs/api/user-client-modules/组件 Tip.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Tip 组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
## 参数说明(Props)
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 说明 |
|
||||
| -------- | ------------------ | -------- | ---------------------------------------- |
|
||||
| `loc` | `ElementLocator` | 必填 | 容器基础定位参数 [x,y,width,height] |
|
||||
| `pad` | `[number, number]` | `[4,4]` | 图标与文字的边距配置 [水平边距,垂直边距] |
|
||||
| `corner` | `number` | `4` | 圆角矩形的圆角半径 |
|
||||
| `id` | `string` | 自动生成 | 提示框唯一标识(需全局唯一) |
|
||||
|
||||
## 暴露接口(Expose)
|
||||
|
||||
| 方法名 | 参数 | 返回值 | 说明 |
|
||||
| --------- | ----------------------------------------------- | ------ | --------------------------------------------------- |
|
||||
| `drawTip` | `text: string`<br>`icon?: AllIds \| AllNumbers` | `void` | 显示带图标的提示文本(图标支持字符串 ID 或数字 ID) |
|
||||
|
||||
## 使用示例
|
||||
|
||||
```tsx
|
||||
import { defineComponent } from 'vue';
|
||||
import { Tip } from './tip';
|
||||
|
||||
// 在游戏界面中定义提示组件
|
||||
export const MyCom = defineComponent(() => {
|
||||
return () => (
|
||||
<container>
|
||||
<Tip
|
||||
loc={[32, 32, 280, 40]}
|
||||
pad={[8, 4]}
|
||||
corner={8}
|
||||
id="global-tip"
|
||||
></Tip>
|
||||
</container>
|
||||
);
|
||||
});
|
||||
|
||||
// 在业务代码中调用提示,使用 TipStore 类
|
||||
const tip = TipStore.get('global-tip');
|
||||
tip?.drawTip('宝箱已解锁!', 'chest_icon');
|
||||
```
|
||||
|
||||
## 特性说明
|
||||
|
||||
1. **自动布局**:
|
||||
|
||||
- 根据图标尺寸自动计算容器宽度
|
||||
- 文字垂直居中显示
|
||||
- 图标与文字间距自动适配
|
||||
|
||||
2. **动画效果**:
|
||||
|
||||
- 默认带有 500ms 双曲正弦缓动的淡入动画
|
||||
- 3 秒无操作后自动淡出
|
||||
- 支持通过`alpha`参数自定义过渡效果
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **全局单例**:建议通过 `TipStore` 进行全局管理,避免重复创建
|
||||
2. **ID 唯一性**:未指定 `id` 时会自动生成格式为 `@default-tip-数字` 的标识
|
||||
3. **自动隐藏**:调用 `drawTip` 后 3 秒自动隐藏,连续调用会重置计时
|
||||
4. **性能优化**:使用 `lodash` 的 `debounce` 进行隐藏操作防抖
|
||||
5. **动画配置**:可通过修改 `hyper('sin', 'in-out')` 参数调整动画曲线
|
104
docs/api/user-client-modules/组件 Waitbox.md
Normal file
104
docs/api/user-client-modules/组件 Waitbox.md
Normal file
@ -0,0 +1,104 @@
|
||||
# WaitBox 等待框组件 API 文档
|
||||
|
||||
本文档由 `DeepSeek R1` 模型生成并微调。
|
||||
|
||||
---
|
||||
|
||||
## 核心特性
|
||||
|
||||
- **Promise 绑定**:自动监控 `Promise` 状态
|
||||
- **复合式组件**:集成背景+文字+加载动画
|
||||
- **双重控制**:支持自动/手动完成等待
|
||||
- **动态布局**:根据内容自动计算高度
|
||||
|
||||
---
|
||||
|
||||
## 组件定位
|
||||
|
||||
> 💡 更推荐使用 `waitbox` 工具函数,该组件主要用于需要深度定制的场景。参考 [此文档](./functions.md#waitbox)。
|
||||
|
||||
---
|
||||
|
||||
## Props 属性说明
|
||||
|
||||
| 属性名 | 类型 | 必填 | 描述 |
|
||||
| --------- | ---------------- | ---- | ----------------------- |
|
||||
| `promise` | `Promise<T>` | 否 | 要监控的 `Promise` 对象 |
|
||||
| `loc` | `ElementLocator` | 是 | 容器定位 |
|
||||
| `width` | `number` | 是 | 内容区域宽度(像素) |
|
||||
| `text` | `string` | 否 | 等待提示文字 |
|
||||
| `pad` | `number` | `16` | 文字与边缘间距 |
|
||||
|
||||
### 继承属性
|
||||
|
||||
- 支持所有 `Background` 背景属性
|
||||
- 支持所有 `TextContent` 文本属性
|
||||
|
||||
---
|
||||
|
||||
## 事件说明
|
||||
|
||||
| 事件名 | 参数 | 触发时机 |
|
||||
| --------- | ---- | -------------------------- |
|
||||
| `resolve` | `T` | `Promise` 完成时返回结果值 |
|
||||
|
||||
---
|
||||
|
||||
## Exposed Methods 暴露方法
|
||||
|
||||
| 方法名 | 参数 | 描述 |
|
||||
| --------- | --------- | ---------------------------- |
|
||||
| `resolve` | `data: T` | 手动完成等待(立即触发事件) |
|
||||
|
||||
---
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基础组件用法
|
||||
|
||||
```tsx
|
||||
// 等待网络请求
|
||||
const fetchPromise = fetchData();
|
||||
|
||||
<WaitBox
|
||||
promise={fetchPromise}
|
||||
loc={[208, 208, void 0, void 0, 0.5, 0.5]} // 居中定位
|
||||
width={208}
|
||||
text="加载中..."
|
||||
winskin="ui/loading_bg"
|
||||
font={new Font('黑体', 18)}
|
||||
onResolve={data => console.log('收到数据:', data)}
|
||||
/>;
|
||||
```
|
||||
|
||||
### 手动控制示例
|
||||
|
||||
```tsx
|
||||
const waitRef = ref<WaitBoxExpose<number>>();
|
||||
|
||||
// 手动结束等待
|
||||
const forceComplete = () => {
|
||||
waitRef.value?.resolve(Date.now());
|
||||
};
|
||||
|
||||
return () => (
|
||||
<WaitBox
|
||||
ref={waitRef}
|
||||
loc={[100, 100, 400, 200]}
|
||||
width={360}
|
||||
text="点击按钮继续"
|
||||
color="rgba(0,0,0,0.7)"
|
||||
></WaitBox>
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **推荐用法**
|
||||
90% 场景应使用 `waitbox` 函数,以下情况才需要直接使用组件:
|
||||
|
||||
- 需要永久显示的等待界面
|
||||
- 需要组合复杂子组件
|
||||
- 需要复用同一个等待实例
|
@ -1,6 +1,6 @@
|
||||
import { KeyCode } from '@motajs/client-base';
|
||||
import { Hotkey, HotkeyData } from '@motajs/system-action';
|
||||
import type { HeroMover, IMoveController } from '@user/data-state';
|
||||
import { HeroMover, IMoveController } from '@user/data-state';
|
||||
import { Ticker } from 'mutate-animate';
|
||||
import { mainScope } from '@motajs/legacy-ui';
|
||||
|
||||
@ -114,7 +114,7 @@ export class HeroKeyMover {
|
||||
|
||||
this.mover.oneStep(this.moveDir);
|
||||
const controller = this.mover.startMove(false, false, false, true);
|
||||
if (!controller) return;
|
||||
if (!controller) return false;
|
||||
|
||||
this.controller = controller;
|
||||
controller.onEnd.then(() => {
|
||||
|
@ -106,7 +106,7 @@ export abstract class AudioDecoder {
|
||||
} else {
|
||||
const decoder = new Decoder();
|
||||
await decoder.create();
|
||||
const decodedData = await decoder.decode(data);
|
||||
const decodedData = await decoder.decodeAll(data);
|
||||
if (!decodedData) return null;
|
||||
const buffer = player.ac.createBuffer(
|
||||
decodedData.channelData.length,
|
||||
@ -150,7 +150,7 @@ export abstract class AudioDecoder {
|
||||
abstract flush(): Promise<IAudioDecodeData | undefined>;
|
||||
}
|
||||
|
||||
export class VorbisDecoder implements AudioDecoder {
|
||||
export class VorbisDecoder extends AudioDecoder {
|
||||
decoder?: OggVorbisDecoderWebWorker;
|
||||
|
||||
async create(): Promise<void> {
|
||||
@ -175,7 +175,7 @@ export class VorbisDecoder implements AudioDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpusDecoder implements AudioDecoder {
|
||||
export class OpusDecoder extends AudioDecoder {
|
||||
decoder?: OggOpusDecoderWebWorker;
|
||||
|
||||
async create(): Promise<void> {
|
||||
|
@ -156,7 +156,7 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
||||
* |-----------|
|
||||
* ```
|
||||
*/
|
||||
createDelay() {
|
||||
createDelayEffect() {
|
||||
return new DelayEffect(this.ac);
|
||||
}
|
||||
|
||||
@ -209,6 +209,10 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
||||
* @param id 要移除的播放路由的名称
|
||||
*/
|
||||
removeRoute(id: string) {
|
||||
const route = this.audioRoutes.get(id);
|
||||
if (route) {
|
||||
route.destroy();
|
||||
}
|
||||
this.audioRoutes.delete(id);
|
||||
}
|
||||
|
||||
@ -565,6 +569,10 @@ export class AudioRoute
|
||||
this.emit('updateEffect');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.effectRoute.forEach(v => v.disconnect());
|
||||
}
|
||||
|
||||
private setOutput() {
|
||||
const effect = this.effectRoute.at(-1);
|
||||
if (!effect) this.output = this.source.output;
|
||||
|
@ -131,4 +131,5 @@ export class SoundPlayer<
|
||||
this.playing.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export const soundPlayer = new SoundPlayer<SoundIds>(audioPlayer);
|
||||
|
@ -112,7 +112,7 @@ export class AudioStreamSource extends AudioSource implements IStreamReader {
|
||||
/** 音频解析器 */
|
||||
private parser?: CodecParser;
|
||||
/** 每多长时间组成一个缓存 Float32Array */
|
||||
private bufferChunkSize = 10;
|
||||
private bufferChunkSize: number = 10;
|
||||
/** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */
|
||||
private audioData: Float32Array[][] = [];
|
||||
|
||||
|
@ -468,7 +468,7 @@ export const Choices = defineComponent<
|
||||
<Background
|
||||
loc={[0, 0, props.width, boxHeight.value]}
|
||||
winskin={props.winskin}
|
||||
color={props.color}
|
||||
color={props.color ?? '#333'}
|
||||
border={props.border}
|
||||
/>
|
||||
{hasTitle.value && (
|
||||
@ -515,6 +515,7 @@ export const Choices = defineComponent<
|
||||
font={props.selFont}
|
||||
cursor="pointer"
|
||||
zIndex={5}
|
||||
fillStyle={props.selFill}
|
||||
onClick={() => emit('choose', v[0])}
|
||||
onSetText={(_, width, height) =>
|
||||
updateChoiceSize(i, width, height)
|
||||
|
@ -14,6 +14,7 @@ import { transitioned } from '../use';
|
||||
import { hyper } from 'mutate-animate';
|
||||
import { logger } from '@motajs/common';
|
||||
import { GameUI, IUIMountable } from '@motajs/system-ui';
|
||||
import { clamp } from 'lodash-es';
|
||||
|
||||
interface ProgressProps extends DefaultProps {
|
||||
/** 进度条的位置 */
|
||||
@ -57,9 +58,10 @@ export const Progress = defineComponent<ProgressProps>(props => {
|
||||
ctx.moveTo(lineWidth, height / 2);
|
||||
ctx.lineTo(width - lineWidth, height / 2);
|
||||
ctx.stroke();
|
||||
if (!isNaN(props.progress)) {
|
||||
const progress = clamp(props.progress, 0, 1);
|
||||
if (!isNaN(progress)) {
|
||||
ctx.strokeStyle = props.success ?? 'green';
|
||||
const p = lineWidth + (width - lineWidth * 2) * props.progress;
|
||||
const p = lineWidth + (width - lineWidth * 2) * progress;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lineWidth, height / 2);
|
||||
ctx.lineTo(p, height / 2);
|
||||
@ -137,7 +139,7 @@ export const Arrow = defineComponent<ArrowProps>(props => {
|
||||
);
|
||||
}, arrowProps);
|
||||
|
||||
export interface ScrollTextProps extends TextboxProps, ScrollProps {
|
||||
export interface ScrollTextProps extends TextContentProps, ScrollProps {
|
||||
/** 自动滚动的速度,每秒多少像素 */
|
||||
speed: number;
|
||||
/** 文字的最大宽度 */
|
||||
@ -232,6 +234,7 @@ export const ScrollText = defineComponent<
|
||||
emit('scrollEnd');
|
||||
paused = true;
|
||||
}
|
||||
lastFixedTime = now;
|
||||
});
|
||||
|
||||
const pause = () => {
|
||||
|
@ -33,7 +33,7 @@ export type PageEmits = {
|
||||
export interface PageExpose {
|
||||
/**
|
||||
* 切换页码
|
||||
* @param page 要切换至的页码数,1 表示第一页
|
||||
* @param page 要切换至的页码数,0 表示第一页
|
||||
*/
|
||||
changePage(page: number): void;
|
||||
|
||||
|
@ -41,15 +41,27 @@ export interface TextContentProps
|
||||
fill?: boolean;
|
||||
/** 是否描边 */
|
||||
stroke?: boolean;
|
||||
/** 是否自适应高度 */
|
||||
/** 是否自适应高度,即组件内部计算 height 的值,而非指定,可与滚动条结合 */
|
||||
autoHeight?: boolean;
|
||||
/** 文字的最大宽度 */
|
||||
width: number;
|
||||
}
|
||||
|
||||
export type TextContentEmits = {
|
||||
/**
|
||||
* 当打字机结束时触发
|
||||
*/
|
||||
typeEnd: () => void;
|
||||
|
||||
/**
|
||||
* 当打字机开始打字时触发
|
||||
*/
|
||||
typeStart: () => void;
|
||||
|
||||
/**
|
||||
* 当文字发生变动,组件内部重新计算文字高度时触发
|
||||
* @param height 更新后的高度
|
||||
*/
|
||||
updateHeight: (height: number) => void;
|
||||
};
|
||||
|
||||
@ -290,7 +302,7 @@ export const Textbox = defineComponent<
|
||||
TextboxEmits,
|
||||
keyof TextboxEmits,
|
||||
TextboxSlots
|
||||
>((props, { slots, expose }) => {
|
||||
>((props, { slots, expose, emit }) => {
|
||||
const contentData = shallowReactive<TextContentProps>({ width: 200 });
|
||||
const data = shallowReactive<TextboxProps>({ width: 200 });
|
||||
|
||||
@ -403,10 +415,12 @@ export const Textbox = defineComponent<
|
||||
|
||||
const onTypeStart = () => {
|
||||
store.emitTypeStart();
|
||||
emit('typeStart');
|
||||
};
|
||||
|
||||
const onTypeEnd = () => {
|
||||
store.emitTypeEnd();
|
||||
emit('typeEnd');
|
||||
};
|
||||
|
||||
expose<TextboxExpose>({
|
||||
|
@ -669,7 +669,7 @@ export class TextContentParser {
|
||||
font: this.font,
|
||||
fontSize: this.status.fontSize,
|
||||
fillStyle: this.status.fillStyle,
|
||||
wait,
|
||||
wait: wait * this.config.interval,
|
||||
splitLines: [],
|
||||
wordBreak: []
|
||||
};
|
||||
|
@ -70,7 +70,25 @@ export function onLoaded(hook: () => void) {
|
||||
export interface ITransitionedController<T> {
|
||||
readonly ref: Ref<T>;
|
||||
readonly value: T;
|
||||
|
||||
/**
|
||||
* 执行动画,使当前值缓慢变化至目标值
|
||||
* @param value 目标值
|
||||
* @param time 动画时长
|
||||
*/
|
||||
set(value: T, time?: number): void;
|
||||
|
||||
/**
|
||||
* 设置动画的速率曲线
|
||||
* @param timing 速率曲线
|
||||
*/
|
||||
mode(timing: TimingFn): void;
|
||||
|
||||
/**
|
||||
* 设置动画的动画时长
|
||||
* @param time 动画时长
|
||||
*/
|
||||
setTime(time: number): void;
|
||||
}
|
||||
|
||||
class RenderTransition implements ITransitionedController<number> {
|
||||
@ -90,8 +108,8 @@ class RenderTransition implements ITransitionedController<number> {
|
||||
constructor(
|
||||
value: number,
|
||||
public readonly transition: Transition,
|
||||
public readonly time: number,
|
||||
public readonly curve: TimingFn
|
||||
public time: number,
|
||||
public curve: TimingFn
|
||||
) {
|
||||
this.ref = ref(value);
|
||||
transition.value[this.key] = value;
|
||||
@ -103,6 +121,14 @@ class RenderTransition implements ITransitionedController<number> {
|
||||
set(value: number, time: number = this.time): void {
|
||||
this.transition.time(time).mode(this.curve).transition(this.key, value);
|
||||
}
|
||||
|
||||
mode(timing: TimingFn): void {
|
||||
this.curve = timing;
|
||||
}
|
||||
|
||||
setTime(time: number): void {
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
type ColorRGBA = [number, number, number, number];
|
||||
@ -127,8 +153,8 @@ class RenderColorTransition implements ITransitionedController<string> {
|
||||
constructor(
|
||||
value: string,
|
||||
public readonly transition: Transition,
|
||||
public readonly time: number,
|
||||
public readonly curve: TimingFn
|
||||
public time: number,
|
||||
public curve: TimingFn
|
||||
) {
|
||||
this.ref = ref(value);
|
||||
const [r, g, b, a] = this.decodeColor(value);
|
||||
@ -145,6 +171,14 @@ class RenderColorTransition implements ITransitionedController<string> {
|
||||
this.transitionColor(this.decodeColor(value), time);
|
||||
}
|
||||
|
||||
mode(timing: TimingFn): void {
|
||||
this.curve = timing;
|
||||
}
|
||||
|
||||
setTime(time: number): void {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
private transitionColor([r, g, b, a]: ColorRGBA, time: number) {
|
||||
this.transition
|
||||
.mode(this.curve)
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './saves';
|
||||
export * from './use';
|
||||
|
14
packages-user/client-modules/src/utils/use.ts
Normal file
14
packages-user/client-modules/src/utils/use.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { getCurrentInstance, onUnmounted } from 'vue';
|
||||
import { WeatherController } from '../weather';
|
||||
|
||||
let weatherId = 0;
|
||||
|
||||
export function useWeather(): [WeatherController] {
|
||||
const weather = new WeatherController(`@weather-${weatherId}`);
|
||||
|
||||
onUnmounted(() => {
|
||||
weather.destroy();
|
||||
});
|
||||
|
||||
return [weather];
|
||||
}
|
@ -117,6 +117,7 @@ export class WeatherController {
|
||||
*/
|
||||
destroy() {
|
||||
WeatherController.map.delete(this.id);
|
||||
this.clearWeather();
|
||||
}
|
||||
|
||||
static get(id: string) {
|
||||
|
Loading…
Reference in New Issue
Block a user