mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-30 03:03:24 +08:00
docs: @user/client-modules & fix: 修复 client-modules 中的一些错误
This commit is contained in:
parent
1d62a404b7
commit
0a2cdbee79
docs/api
motajs-legacy-common
user-client-modules
AudioDecoder.mdAudioEffect.mdAudioPlayer.mdAudioRoute.mdAudioSource.mdBgmController.mdHeroKeyMover.mdSoundPlayer.mdStreamLoader.mdTextContentParser.mdTextContentTyper.mdTextboxStore.mdTipStore.mdWeatherController.mdfunctions.md图标组件.md组件 Arrow.md组件 Background.md组件 Choices.md组件 ConfirmBox.md组件 Page.md组件 Progress.md组件 Scroll.md组件 ScrollText.md组件 Selection.md组件 TextContent.md组件 Textbox.md组件 Tip.md组件 Waitbox.md
packages-user/client-modules/src
action
audio
render
utils
weather
@ -25,7 +25,7 @@
|
|||||||
## 构造方法
|
## 构造方法
|
||||||
|
|
||||||
```typescript
|
```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 { KeyCode } from '@motajs/client-base';
|
||||||
import { Hotkey, HotkeyData } from '@motajs/system-action';
|
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 { Ticker } from 'mutate-animate';
|
||||||
import { mainScope } from '@motajs/legacy-ui';
|
import { mainScope } from '@motajs/legacy-ui';
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ export class HeroKeyMover {
|
|||||||
|
|
||||||
this.mover.oneStep(this.moveDir);
|
this.mover.oneStep(this.moveDir);
|
||||||
const controller = this.mover.startMove(false, false, false, true);
|
const controller = this.mover.startMove(false, false, false, true);
|
||||||
if (!controller) return;
|
if (!controller) return false;
|
||||||
|
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
controller.onEnd.then(() => {
|
controller.onEnd.then(() => {
|
||||||
|
@ -106,7 +106,7 @@ export abstract class AudioDecoder {
|
|||||||
} else {
|
} else {
|
||||||
const decoder = new Decoder();
|
const decoder = new Decoder();
|
||||||
await decoder.create();
|
await decoder.create();
|
||||||
const decodedData = await decoder.decode(data);
|
const decodedData = await decoder.decodeAll(data);
|
||||||
if (!decodedData) return null;
|
if (!decodedData) return null;
|
||||||
const buffer = player.ac.createBuffer(
|
const buffer = player.ac.createBuffer(
|
||||||
decodedData.channelData.length,
|
decodedData.channelData.length,
|
||||||
@ -150,7 +150,7 @@ export abstract class AudioDecoder {
|
|||||||
abstract flush(): Promise<IAudioDecodeData | undefined>;
|
abstract flush(): Promise<IAudioDecodeData | undefined>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VorbisDecoder implements AudioDecoder {
|
export class VorbisDecoder extends AudioDecoder {
|
||||||
decoder?: OggVorbisDecoderWebWorker;
|
decoder?: OggVorbisDecoderWebWorker;
|
||||||
|
|
||||||
async create(): Promise<void> {
|
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;
|
decoder?: OggOpusDecoderWebWorker;
|
||||||
|
|
||||||
async create(): Promise<void> {
|
async create(): Promise<void> {
|
||||||
|
@ -156,7 +156,7 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
|||||||
* |-----------|
|
* |-----------|
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
createDelay() {
|
createDelayEffect() {
|
||||||
return new DelayEffect(this.ac);
|
return new DelayEffect(this.ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +209,10 @@ export class AudioPlayer extends EventEmitter<AudioPlayerEvent> {
|
|||||||
* @param id 要移除的播放路由的名称
|
* @param id 要移除的播放路由的名称
|
||||||
*/
|
*/
|
||||||
removeRoute(id: string) {
|
removeRoute(id: string) {
|
||||||
|
const route = this.audioRoutes.get(id);
|
||||||
|
if (route) {
|
||||||
|
route.destroy();
|
||||||
|
}
|
||||||
this.audioRoutes.delete(id);
|
this.audioRoutes.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +569,10 @@ export class AudioRoute
|
|||||||
this.emit('updateEffect');
|
this.emit('updateEffect');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.effectRoute.forEach(v => v.disconnect());
|
||||||
|
}
|
||||||
|
|
||||||
private setOutput() {
|
private setOutput() {
|
||||||
const effect = this.effectRoute.at(-1);
|
const effect = this.effectRoute.at(-1);
|
||||||
if (!effect) this.output = this.source.output;
|
if (!effect) this.output = this.source.output;
|
||||||
|
@ -131,4 +131,5 @@ export class SoundPlayer<
|
|||||||
this.playing.clear();
|
this.playing.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const soundPlayer = new SoundPlayer<SoundIds>(audioPlayer);
|
export const soundPlayer = new SoundPlayer<SoundIds>(audioPlayer);
|
||||||
|
@ -112,7 +112,7 @@ export class AudioStreamSource extends AudioSource implements IStreamReader {
|
|||||||
/** 音频解析器 */
|
/** 音频解析器 */
|
||||||
private parser?: CodecParser;
|
private parser?: CodecParser;
|
||||||
/** 每多长时间组成一个缓存 Float32Array */
|
/** 每多长时间组成一个缓存 Float32Array */
|
||||||
private bufferChunkSize = 10;
|
private bufferChunkSize: number = 10;
|
||||||
/** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */
|
/** 缓存音频数据,每 bufferChunkSize 秒钟组成一个 Float32Array,用于流式解码 */
|
||||||
private audioData: Float32Array[][] = [];
|
private audioData: Float32Array[][] = [];
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ export const Choices = defineComponent<
|
|||||||
<Background
|
<Background
|
||||||
loc={[0, 0, props.width, boxHeight.value]}
|
loc={[0, 0, props.width, boxHeight.value]}
|
||||||
winskin={props.winskin}
|
winskin={props.winskin}
|
||||||
color={props.color}
|
color={props.color ?? '#333'}
|
||||||
border={props.border}
|
border={props.border}
|
||||||
/>
|
/>
|
||||||
{hasTitle.value && (
|
{hasTitle.value && (
|
||||||
@ -515,6 +515,7 @@ export const Choices = defineComponent<
|
|||||||
font={props.selFont}
|
font={props.selFont}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
zIndex={5}
|
zIndex={5}
|
||||||
|
fillStyle={props.selFill}
|
||||||
onClick={() => emit('choose', v[0])}
|
onClick={() => emit('choose', v[0])}
|
||||||
onSetText={(_, width, height) =>
|
onSetText={(_, width, height) =>
|
||||||
updateChoiceSize(i, width, height)
|
updateChoiceSize(i, width, height)
|
||||||
|
@ -14,6 +14,7 @@ import { transitioned } from '../use';
|
|||||||
import { hyper } from 'mutate-animate';
|
import { hyper } from 'mutate-animate';
|
||||||
import { logger } from '@motajs/common';
|
import { logger } from '@motajs/common';
|
||||||
import { GameUI, IUIMountable } from '@motajs/system-ui';
|
import { GameUI, IUIMountable } from '@motajs/system-ui';
|
||||||
|
import { clamp } from 'lodash-es';
|
||||||
|
|
||||||
interface ProgressProps extends DefaultProps {
|
interface ProgressProps extends DefaultProps {
|
||||||
/** 进度条的位置 */
|
/** 进度条的位置 */
|
||||||
@ -57,9 +58,10 @@ export const Progress = defineComponent<ProgressProps>(props => {
|
|||||||
ctx.moveTo(lineWidth, height / 2);
|
ctx.moveTo(lineWidth, height / 2);
|
||||||
ctx.lineTo(width - lineWidth, height / 2);
|
ctx.lineTo(width - lineWidth, height / 2);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
if (!isNaN(props.progress)) {
|
const progress = clamp(props.progress, 0, 1);
|
||||||
|
if (!isNaN(progress)) {
|
||||||
ctx.strokeStyle = props.success ?? 'green';
|
ctx.strokeStyle = props.success ?? 'green';
|
||||||
const p = lineWidth + (width - lineWidth * 2) * props.progress;
|
const p = lineWidth + (width - lineWidth * 2) * progress;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(lineWidth, height / 2);
|
ctx.moveTo(lineWidth, height / 2);
|
||||||
ctx.lineTo(p, height / 2);
|
ctx.lineTo(p, height / 2);
|
||||||
@ -137,7 +139,7 @@ export const Arrow = defineComponent<ArrowProps>(props => {
|
|||||||
);
|
);
|
||||||
}, arrowProps);
|
}, arrowProps);
|
||||||
|
|
||||||
export interface ScrollTextProps extends TextboxProps, ScrollProps {
|
export interface ScrollTextProps extends TextContentProps, ScrollProps {
|
||||||
/** 自动滚动的速度,每秒多少像素 */
|
/** 自动滚动的速度,每秒多少像素 */
|
||||||
speed: number;
|
speed: number;
|
||||||
/** 文字的最大宽度 */
|
/** 文字的最大宽度 */
|
||||||
@ -232,6 +234,7 @@ export const ScrollText = defineComponent<
|
|||||||
emit('scrollEnd');
|
emit('scrollEnd');
|
||||||
paused = true;
|
paused = true;
|
||||||
}
|
}
|
||||||
|
lastFixedTime = now;
|
||||||
});
|
});
|
||||||
|
|
||||||
const pause = () => {
|
const pause = () => {
|
||||||
|
@ -33,7 +33,7 @@ export type PageEmits = {
|
|||||||
export interface PageExpose {
|
export interface PageExpose {
|
||||||
/**
|
/**
|
||||||
* 切换页码
|
* 切换页码
|
||||||
* @param page 要切换至的页码数,1 表示第一页
|
* @param page 要切换至的页码数,0 表示第一页
|
||||||
*/
|
*/
|
||||||
changePage(page: number): void;
|
changePage(page: number): void;
|
||||||
|
|
||||||
|
@ -41,15 +41,27 @@ export interface TextContentProps
|
|||||||
fill?: boolean;
|
fill?: boolean;
|
||||||
/** 是否描边 */
|
/** 是否描边 */
|
||||||
stroke?: boolean;
|
stroke?: boolean;
|
||||||
/** 是否自适应高度 */
|
/** 是否自适应高度,即组件内部计算 height 的值,而非指定,可与滚动条结合 */
|
||||||
autoHeight?: boolean;
|
autoHeight?: boolean;
|
||||||
/** 文字的最大宽度 */
|
/** 文字的最大宽度 */
|
||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TextContentEmits = {
|
export type TextContentEmits = {
|
||||||
|
/**
|
||||||
|
* 当打字机结束时触发
|
||||||
|
*/
|
||||||
typeEnd: () => void;
|
typeEnd: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当打字机开始打字时触发
|
||||||
|
*/
|
||||||
typeStart: () => void;
|
typeStart: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当文字发生变动,组件内部重新计算文字高度时触发
|
||||||
|
* @param height 更新后的高度
|
||||||
|
*/
|
||||||
updateHeight: (height: number) => void;
|
updateHeight: (height: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -290,7 +302,7 @@ export const Textbox = defineComponent<
|
|||||||
TextboxEmits,
|
TextboxEmits,
|
||||||
keyof TextboxEmits,
|
keyof TextboxEmits,
|
||||||
TextboxSlots
|
TextboxSlots
|
||||||
>((props, { slots, expose }) => {
|
>((props, { slots, expose, emit }) => {
|
||||||
const contentData = shallowReactive<TextContentProps>({ width: 200 });
|
const contentData = shallowReactive<TextContentProps>({ width: 200 });
|
||||||
const data = shallowReactive<TextboxProps>({ width: 200 });
|
const data = shallowReactive<TextboxProps>({ width: 200 });
|
||||||
|
|
||||||
@ -403,10 +415,12 @@ export const Textbox = defineComponent<
|
|||||||
|
|
||||||
const onTypeStart = () => {
|
const onTypeStart = () => {
|
||||||
store.emitTypeStart();
|
store.emitTypeStart();
|
||||||
|
emit('typeStart');
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTypeEnd = () => {
|
const onTypeEnd = () => {
|
||||||
store.emitTypeEnd();
|
store.emitTypeEnd();
|
||||||
|
emit('typeEnd');
|
||||||
};
|
};
|
||||||
|
|
||||||
expose<TextboxExpose>({
|
expose<TextboxExpose>({
|
||||||
|
@ -669,7 +669,7 @@ export class TextContentParser {
|
|||||||
font: this.font,
|
font: this.font,
|
||||||
fontSize: this.status.fontSize,
|
fontSize: this.status.fontSize,
|
||||||
fillStyle: this.status.fillStyle,
|
fillStyle: this.status.fillStyle,
|
||||||
wait,
|
wait: wait * this.config.interval,
|
||||||
splitLines: [],
|
splitLines: [],
|
||||||
wordBreak: []
|
wordBreak: []
|
||||||
};
|
};
|
||||||
|
@ -70,7 +70,25 @@ export function onLoaded(hook: () => void) {
|
|||||||
export interface ITransitionedController<T> {
|
export interface ITransitionedController<T> {
|
||||||
readonly ref: Ref<T>;
|
readonly ref: Ref<T>;
|
||||||
readonly value: T;
|
readonly value: T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行动画,使当前值缓慢变化至目标值
|
||||||
|
* @param value 目标值
|
||||||
|
* @param time 动画时长
|
||||||
|
*/
|
||||||
set(value: T, time?: number): void;
|
set(value: T, time?: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置动画的速率曲线
|
||||||
|
* @param timing 速率曲线
|
||||||
|
*/
|
||||||
|
mode(timing: TimingFn): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置动画的动画时长
|
||||||
|
* @param time 动画时长
|
||||||
|
*/
|
||||||
|
setTime(time: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderTransition implements ITransitionedController<number> {
|
class RenderTransition implements ITransitionedController<number> {
|
||||||
@ -90,8 +108,8 @@ class RenderTransition implements ITransitionedController<number> {
|
|||||||
constructor(
|
constructor(
|
||||||
value: number,
|
value: number,
|
||||||
public readonly transition: Transition,
|
public readonly transition: Transition,
|
||||||
public readonly time: number,
|
public time: number,
|
||||||
public readonly curve: TimingFn
|
public curve: TimingFn
|
||||||
) {
|
) {
|
||||||
this.ref = ref(value);
|
this.ref = ref(value);
|
||||||
transition.value[this.key] = value;
|
transition.value[this.key] = value;
|
||||||
@ -103,6 +121,14 @@ class RenderTransition implements ITransitionedController<number> {
|
|||||||
set(value: number, time: number = this.time): void {
|
set(value: number, time: number = this.time): void {
|
||||||
this.transition.time(time).mode(this.curve).transition(this.key, value);
|
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];
|
type ColorRGBA = [number, number, number, number];
|
||||||
@ -127,8 +153,8 @@ class RenderColorTransition implements ITransitionedController<string> {
|
|||||||
constructor(
|
constructor(
|
||||||
value: string,
|
value: string,
|
||||||
public readonly transition: Transition,
|
public readonly transition: Transition,
|
||||||
public readonly time: number,
|
public time: number,
|
||||||
public readonly curve: TimingFn
|
public curve: TimingFn
|
||||||
) {
|
) {
|
||||||
this.ref = ref(value);
|
this.ref = ref(value);
|
||||||
const [r, g, b, a] = this.decodeColor(value);
|
const [r, g, b, a] = this.decodeColor(value);
|
||||||
@ -145,6 +171,14 @@ class RenderColorTransition implements ITransitionedController<string> {
|
|||||||
this.transitionColor(this.decodeColor(value), time);
|
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) {
|
private transitionColor([r, g, b, a]: ColorRGBA, time: number) {
|
||||||
this.transition
|
this.transition
|
||||||
.mode(this.curve)
|
.mode(this.curve)
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './saves';
|
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() {
|
destroy() {
|
||||||
WeatherController.map.delete(this.id);
|
WeatherController.map.delete(this.id);
|
||||||
|
this.clearWeather();
|
||||||
}
|
}
|
||||||
|
|
||||||
static get(id: string) {
|
static get(id: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user