interaction-system 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +377 -0
- package/USAGE.md +490 -0
- package/dist/index.cjs.js +1983 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +1983 -0
- package/dist/index.es.js.map +1 -0
- package/dist/input/KeyEvent.d.ts +315 -0
- package/dist/input/KeyEvent.d.ts.map +1 -0
- package/dist/input/KeyInputHandler.d.ts +15 -0
- package/dist/input/KeyInputHandler.d.ts.map +1 -0
- package/dist/input/KeyToCodeMap.d.ts +2 -0
- package/dist/input/KeyToCodeMap.d.ts.map +1 -0
- package/dist/input/UIEventsCode.d.ts +175 -0
- package/dist/input/UIEventsCode.d.ts.map +1 -0
- package/dist/interaction/FeaturedInteractionHandler.d.ts +26 -0
- package/dist/interaction/FeaturedInteractionHandler.d.ts.map +1 -0
- package/dist/interaction/InteractionHandler.d.ts +86 -0
- package/dist/interaction/InteractionHandler.d.ts.map +1 -0
- package/dist/interaction/SimpleInteractionHandler.d.ts +19 -0
- package/dist/interaction/SimpleInteractionHandler.d.ts.map +1 -0
- package/dist/messages/CommandControlMessage.d.ts +49 -0
- package/dist/messages/CommandControlMessage.d.ts.map +1 -0
- package/dist/messages/ControlMessage.d.ts +25 -0
- package/dist/messages/ControlMessage.d.ts.map +1 -0
- package/dist/messages/KeyCodeControlMessage.d.ts +24 -0
- package/dist/messages/KeyCodeControlMessage.d.ts.map +1 -0
- package/dist/messages/ScrollControlMessage.d.ts +23 -0
- package/dist/messages/ScrollControlMessage.d.ts.map +1 -0
- package/dist/messages/TextControlMessage.d.ts +19 -0
- package/dist/messages/TextControlMessage.d.ts.map +1 -0
- package/dist/messages/TouchControlMessage.d.ts +44 -0
- package/dist/messages/TouchControlMessage.d.ts.map +1 -0
- package/dist/models/MotionEvent.d.ts +18 -0
- package/dist/models/MotionEvent.d.ts.map +1 -0
- package/dist/models/Point.d.ts +14 -0
- package/dist/models/Point.d.ts.map +1 -0
- package/dist/models/Position.d.ts +17 -0
- package/dist/models/Position.d.ts.map +1 -0
- package/dist/models/Rect.d.ts +22 -0
- package/dist/models/Rect.d.ts.map +1 -0
- package/dist/models/ScreenInfo.d.ts +14 -0
- package/dist/models/ScreenInfo.d.ts.map +1 -0
- package/dist/models/Size.d.ts +21 -0
- package/dist/models/Size.d.ts.map +1 -0
- package/dist/types/PlayerInterface.d.ts +20 -0
- package/dist/types/PlayerInterface.d.ts.map +1 -0
- package/dist/utils/Util.d.ts +26 -0
- package/dist/utils/Util.d.ts.map +1 -0
- package/package.json +56 -0
package/USAGE.md
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# Interaction System 使用指南
|
|
2
|
+
|
|
3
|
+
本文档提供 interaction-system 库的详细使用说明和最佳实践。
|
|
4
|
+
|
|
5
|
+
## 目录
|
|
6
|
+
|
|
7
|
+
- [快速开始](#快速开始)
|
|
8
|
+
- [核心概念](#核心概念)
|
|
9
|
+
- [完整示例](#完整示例)
|
|
10
|
+
- [构建工具配置](#构建工具配置)
|
|
11
|
+
- [API 参考](#api-参考)
|
|
12
|
+
- [常见问题](#常见问题)
|
|
13
|
+
|
|
14
|
+
## 快速开始
|
|
15
|
+
|
|
16
|
+
### 1. 安装依赖
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 如果使用 npm link
|
|
20
|
+
cd interaction-system && npm link
|
|
21
|
+
cd your-project && npm link interaction-system
|
|
22
|
+
|
|
23
|
+
# 或者使用 file: 协议
|
|
24
|
+
npm install file:../path/to/interaction-system
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. 基本设置
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import {
|
|
31
|
+
FeaturedInteractionHandler,
|
|
32
|
+
InteractionHandlerListener,
|
|
33
|
+
ControlMessage,
|
|
34
|
+
ScreenInfo,
|
|
35
|
+
Rect,
|
|
36
|
+
Size,
|
|
37
|
+
IPlayer
|
|
38
|
+
} from 'interaction-system';
|
|
39
|
+
|
|
40
|
+
// 实现 IPlayer 接口
|
|
41
|
+
class VideoPlayer implements IPlayer {
|
|
42
|
+
private canvas: HTMLCanvasElement;
|
|
43
|
+
private screenInfo?: ScreenInfo;
|
|
44
|
+
|
|
45
|
+
constructor(canvas: HTMLCanvasElement) {
|
|
46
|
+
this.canvas = canvas;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getTouchableElement(): HTMLCanvasElement {
|
|
50
|
+
return this.canvas;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getScreenInfo(): ScreenInfo | undefined {
|
|
54
|
+
return this.screenInfo;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setScreenInfo(info: ScreenInfo) {
|
|
58
|
+
this.screenInfo = info;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 创建 canvas 元素
|
|
63
|
+
const canvas = document.createElement('canvas');
|
|
64
|
+
canvas.width = 360;
|
|
65
|
+
canvas.height = 640;
|
|
66
|
+
document.body.appendChild(canvas);
|
|
67
|
+
|
|
68
|
+
// 初始化播放器
|
|
69
|
+
const player = new VideoPlayer(canvas);
|
|
70
|
+
|
|
71
|
+
// 设置屏幕信息
|
|
72
|
+
const screenInfo = new ScreenInfo(
|
|
73
|
+
new Rect(0, 0, 1080, 1920),
|
|
74
|
+
new Size(1080, 1920),
|
|
75
|
+
0
|
|
76
|
+
);
|
|
77
|
+
player.setScreenInfo(screenInfo);
|
|
78
|
+
|
|
79
|
+
// 创建消息监听器
|
|
80
|
+
const listener: InteractionHandlerListener = {
|
|
81
|
+
sendMessage: (message: ControlMessage) => {
|
|
82
|
+
console.log('控制消息:', message.toString());
|
|
83
|
+
const buffer = message.toBuffer();
|
|
84
|
+
// 发送到服务器
|
|
85
|
+
// websocket.send(buffer);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// 创建交互处理器
|
|
90
|
+
const handler = new FeaturedInteractionHandler(player, listener);
|
|
91
|
+
|
|
92
|
+
// 清理(当不再需要时)
|
|
93
|
+
// handler.release();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 核心概念
|
|
97
|
+
|
|
98
|
+
### IPlayer 接口
|
|
99
|
+
|
|
100
|
+
`IPlayer` 是交互系统的核心接口,定义了与播放器交互的契约:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
interface IPlayer {
|
|
104
|
+
// 获取用于捕获交互事件的 canvas 元素
|
|
105
|
+
getTouchableElement(): HTMLCanvasElement;
|
|
106
|
+
|
|
107
|
+
// 获取当前屏幕信息(用于坐标转换)
|
|
108
|
+
getScreenInfo(): ScreenInfo | undefined;
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### ScreenInfo
|
|
113
|
+
|
|
114
|
+
屏幕信息用于将浏览器坐标转换为设备坐标:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const screenInfo = new ScreenInfo(
|
|
118
|
+
new Rect(0, 0, 1080, 1920), // 内容区域
|
|
119
|
+
new Size(1080, 1920), // 视频尺寸
|
|
120
|
+
0 // 设备旋转(0, 1, 2, 3)
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 控制消息
|
|
125
|
+
|
|
126
|
+
所有交互都会转换为控制消息:
|
|
127
|
+
|
|
128
|
+
- **TouchControlMessage** - 触摸事件
|
|
129
|
+
- **ScrollControlMessage** - 滚动事件
|
|
130
|
+
- **KeyCodeControlMessage** - 键盘按键
|
|
131
|
+
- **TextControlMessage** - 文本输入
|
|
132
|
+
- **CommandControlMessage** - 系统命令
|
|
133
|
+
|
|
134
|
+
## 完整示例
|
|
135
|
+
|
|
136
|
+
### 示例 1: 基础触摸交互
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
import {
|
|
140
|
+
FeaturedInteractionHandler,
|
|
141
|
+
TouchControlMessage,
|
|
142
|
+
MotionEvent
|
|
143
|
+
} from 'interaction-system';
|
|
144
|
+
|
|
145
|
+
// ... 设置 player 和 listener ...
|
|
146
|
+
|
|
147
|
+
const handler = new FeaturedInteractionHandler(player, {
|
|
148
|
+
sendMessage: (message) => {
|
|
149
|
+
if (message instanceof TouchControlMessage) {
|
|
150
|
+
const { action, pointerId, position } = message;
|
|
151
|
+
console.log(`触摸事件: ${
|
|
152
|
+
action === MotionEvent.ACTION_DOWN ? '按下' :
|
|
153
|
+
action === MotionEvent.ACTION_UP ? '抬起' : '移动'
|
|
154
|
+
} at (${position.point.x}, ${position.point.y})`);
|
|
155
|
+
}
|
|
156
|
+
// 发送到服务器
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 示例 2: 键盘输入
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { KeyInputHandler, KeyEventListener } from 'interaction-system';
|
|
165
|
+
|
|
166
|
+
const keyListener: KeyEventListener = {
|
|
167
|
+
onKeyEvent: (event) => {
|
|
168
|
+
console.log(`键码: ${event.keycode}, 动作: ${event.action}`);
|
|
169
|
+
const buffer = event.toBuffer();
|
|
170
|
+
// websocket.send(buffer);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// 注册全局键盘监听
|
|
175
|
+
KeyInputHandler.addEventListener(keyListener);
|
|
176
|
+
|
|
177
|
+
// 移除监听(清理时)
|
|
178
|
+
// KeyInputHandler.removeEventListener(keyListener);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 示例 3: 简化版交互处理器
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
SimpleInteractionHandler,
|
|
186
|
+
TouchHandlerListener,
|
|
187
|
+
Position
|
|
188
|
+
} from 'interaction-system';
|
|
189
|
+
|
|
190
|
+
const listener: TouchHandlerListener = {
|
|
191
|
+
performClick: (position: Position) => {
|
|
192
|
+
console.log(`点击位置: (${position.point.x}, ${position.point.y})`);
|
|
193
|
+
},
|
|
194
|
+
performScroll: (from: Position, to: Position) => {
|
|
195
|
+
console.log(`滚动: 从 (${from.point.x}, ${from.point.y}) 到 (${to.point.x}, ${to.point.y})`);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const simpleHandler = new SimpleInteractionHandler(player, listener);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 示例 4: 手动创建控制消息
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import {
|
|
206
|
+
TouchControlMessage,
|
|
207
|
+
Position,
|
|
208
|
+
Point,
|
|
209
|
+
Size,
|
|
210
|
+
MotionEvent
|
|
211
|
+
} from 'interaction-system';
|
|
212
|
+
|
|
213
|
+
// 模拟触摸按下
|
|
214
|
+
function simulateTouch(x: number, y: number) {
|
|
215
|
+
const message = new TouchControlMessage(
|
|
216
|
+
MotionEvent.ACTION_DOWN,
|
|
217
|
+
0, // 指针 ID
|
|
218
|
+
new Position(
|
|
219
|
+
new Point(x, y),
|
|
220
|
+
new Size(1080, 1920)
|
|
221
|
+
),
|
|
222
|
+
1.0, // 压力值
|
|
223
|
+
MotionEvent.BUTTON_PRIMARY
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const buffer = message.toBuffer();
|
|
227
|
+
return buffer;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// 发送到服务器
|
|
231
|
+
const touchBuffer = simulateTouch(540, 960);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 构建工具配置
|
|
235
|
+
|
|
236
|
+
### Vite 配置
|
|
237
|
+
|
|
238
|
+
如果遇到 PNG 图片导入错误,配置 `vite.config.ts`:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { defineConfig } from 'vite';
|
|
242
|
+
|
|
243
|
+
export default defineConfig({
|
|
244
|
+
assetsInclude: ['**/*.png'],
|
|
245
|
+
resolve: {
|
|
246
|
+
alias: {
|
|
247
|
+
'interaction-system': require.resolve('interaction-system')
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Webpack 配置
|
|
254
|
+
|
|
255
|
+
在 `webpack.config.js` 中:
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
module.exports = {
|
|
259
|
+
module: {
|
|
260
|
+
rules: [
|
|
261
|
+
{
|
|
262
|
+
test: /\.(png|jpg|gif)$/,
|
|
263
|
+
type: 'asset/resource'
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
},
|
|
267
|
+
resolve: {
|
|
268
|
+
extensions: ['.ts', '.tsx', '.js']
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### TypeScript 配置
|
|
274
|
+
|
|
275
|
+
确保 `tsconfig.json` 包含:
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"compilerOptions": {
|
|
280
|
+
"esModuleInterop": true,
|
|
281
|
+
"allowSyntheticDefaultImports": true,
|
|
282
|
+
"moduleResolution": "node",
|
|
283
|
+
"resolveJsonModule": true
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## API 参考
|
|
289
|
+
|
|
290
|
+
### 交互处理器
|
|
291
|
+
|
|
292
|
+
#### FeaturedInteractionHandler
|
|
293
|
+
|
|
294
|
+
完整功能的交互处理器。
|
|
295
|
+
|
|
296
|
+
**构造函数:**
|
|
297
|
+
```typescript
|
|
298
|
+
constructor(player: IPlayer, listener: InteractionHandlerListener)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**支持的事件:**
|
|
302
|
+
- 鼠标点击、移动、抬起
|
|
303
|
+
- 触摸开始、移动、结束
|
|
304
|
+
- 鼠标滚轮
|
|
305
|
+
- 键盘按键
|
|
306
|
+
- 多点触控(Ctrl + 鼠标)
|
|
307
|
+
|
|
308
|
+
**多点触控:**
|
|
309
|
+
- `Ctrl + 鼠标移动` - 创建对称的两个触摸点
|
|
310
|
+
- `Ctrl + Shift + 鼠标` - 以首次点击为中心创建镜像触摸点
|
|
311
|
+
|
|
312
|
+
#### SimpleInteractionHandler
|
|
313
|
+
|
|
314
|
+
简化版交互处理器。
|
|
315
|
+
|
|
316
|
+
**构造函数:**
|
|
317
|
+
```typescript
|
|
318
|
+
constructor(player: IPlayer, listener: TouchHandlerListener)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**支持的操作:**
|
|
322
|
+
- 点击(短距离拖动)
|
|
323
|
+
- 滚动(长距离拖动)
|
|
324
|
+
|
|
325
|
+
### 键盘处理
|
|
326
|
+
|
|
327
|
+
#### KeyInputHandler
|
|
328
|
+
|
|
329
|
+
全局键盘事件处理器。
|
|
330
|
+
|
|
331
|
+
**方法:**
|
|
332
|
+
```typescript
|
|
333
|
+
// 添加监听器
|
|
334
|
+
static addEventListener(listener: KeyEventListener): void
|
|
335
|
+
|
|
336
|
+
// 移除监听器
|
|
337
|
+
static removeEventListener(listener: KeyEventListener): void
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 数据模型
|
|
341
|
+
|
|
342
|
+
#### Point
|
|
343
|
+
```typescript
|
|
344
|
+
class Point {
|
|
345
|
+
constructor(x: number, y: number)
|
|
346
|
+
x: number
|
|
347
|
+
y: number
|
|
348
|
+
distance(to: Point): number
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### Size
|
|
353
|
+
```typescript
|
|
354
|
+
class Size {
|
|
355
|
+
constructor(width: number, height: number)
|
|
356
|
+
width: number
|
|
357
|
+
height: number
|
|
358
|
+
rotate(): Size
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Position
|
|
363
|
+
```typescript
|
|
364
|
+
class Position {
|
|
365
|
+
constructor(point: Point, screenSize: Size)
|
|
366
|
+
point: Point
|
|
367
|
+
screenSize: Size
|
|
368
|
+
rotate(rotation: number): Position
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### ScreenInfo
|
|
373
|
+
```typescript
|
|
374
|
+
class ScreenInfo {
|
|
375
|
+
constructor(
|
|
376
|
+
contentRect: Rect,
|
|
377
|
+
videoSize: Size,
|
|
378
|
+
deviceRotation: number
|
|
379
|
+
)
|
|
380
|
+
static fromBuffer(buffer: Buffer): ScreenInfo
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### 控制消息
|
|
385
|
+
|
|
386
|
+
所有控制消息都继承自 `ControlMessage` 并实现:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
abstract class ControlMessage {
|
|
390
|
+
toBuffer(): Buffer // 序列化为 Buffer
|
|
391
|
+
toString(): string // 转换为可读字符串
|
|
392
|
+
toJSON(): object // 转换为 JSON 对象
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## 常见问题
|
|
397
|
+
|
|
398
|
+
### Q: 如何调试控制消息?
|
|
399
|
+
|
|
400
|
+
A: 所有消息都实现了 `toString()` 和 `toJSON()` 方法:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
listener: {
|
|
404
|
+
sendMessage: (msg) => {
|
|
405
|
+
console.log('消息类型:', msg.type);
|
|
406
|
+
console.log('可读格式:', msg.toString());
|
|
407
|
+
console.log('JSON:', JSON.stringify(msg.toJSON(), null, 2));
|
|
408
|
+
console.log('Buffer:', msg.toBuffer());
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Q: 如何处理图片资源错误?
|
|
414
|
+
|
|
415
|
+
A: 图片仅用于多点触控可视化。如果遇到导入错误:
|
|
416
|
+
|
|
417
|
+
1. **配置构建工具**(推荐)- 参见[构建工具配置](#构建工具配置)
|
|
418
|
+
2. **忽略错误** - 功能仍正常,只是没有图片显示
|
|
419
|
+
|
|
420
|
+
### Q: 如何获取实时坐标?
|
|
421
|
+
|
|
422
|
+
A: 在消息监听器中获取:
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
sendMessage: (message) => {
|
|
426
|
+
if (message instanceof TouchControlMessage) {
|
|
427
|
+
const { x, y } = message.position.point;
|
|
428
|
+
console.log(`坐标: (${x}, ${y})`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Q: 如何禁用多点触控?
|
|
434
|
+
|
|
435
|
+
A: 多点触控需要按住 Ctrl 键才激活,用户不按就不会触发。
|
|
436
|
+
|
|
437
|
+
### Q: 如何清理资源?
|
|
438
|
+
|
|
439
|
+
A: 调用 `release()` 方法:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
// 创建
|
|
443
|
+
const handler = new FeaturedInteractionHandler(player, listener);
|
|
444
|
+
|
|
445
|
+
// 清理
|
|
446
|
+
handler.release();
|
|
447
|
+
|
|
448
|
+
// 键盘监听器
|
|
449
|
+
KeyInputHandler.removeEventListener(listener);
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Q: 能否在移动端使用?
|
|
453
|
+
|
|
454
|
+
A: 可以。`FeaturedInteractionHandler` 自动处理触摸事件,在移动设备上会使用原生触摸事件。
|
|
455
|
+
|
|
456
|
+
### Q: 如何修改键盘映射?
|
|
457
|
+
|
|
458
|
+
A: 键盘映射定义在 `KeyToCodeMap` 中。如需自定义,可以创建新的 Map:
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { KeyEvent } from 'interaction-system';
|
|
462
|
+
|
|
463
|
+
const customMap = new Map([
|
|
464
|
+
['KeyA', KeyEvent.KEYCODE_A],
|
|
465
|
+
// ... 添加更多映射
|
|
466
|
+
]);
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Q: 支持哪些浏览器?
|
|
470
|
+
|
|
471
|
+
A: 支持所有现代浏览器:
|
|
472
|
+
- Chrome/Edge 90+
|
|
473
|
+
- Firefox 88+
|
|
474
|
+
- Safari 14+
|
|
475
|
+
- 移动浏览器(iOS Safari, Chrome Mobile)
|
|
476
|
+
|
|
477
|
+
### Q: TypeScript 类型定义在哪里?
|
|
478
|
+
|
|
479
|
+
A: 包自带完整的类型定义文件(`.d.ts`),会被 TypeScript 自动识别。
|
|
480
|
+
|
|
481
|
+
## 更多帮助
|
|
482
|
+
|
|
483
|
+
- [README.md](README.md) - 项目概览和快速开始
|
|
484
|
+
- [examples/](examples/) - 完整示例项目
|
|
485
|
+
- [GitHub Issues](https://github.com/NetrisTV/ws-scrcpy/issues) - 报告问题
|
|
486
|
+
|
|
487
|
+
## 贡献
|
|
488
|
+
|
|
489
|
+
欢迎提交 Issue 和 Pull Request!
|
|
490
|
+
|