@ayden-fc2/riffle-bridge-web 1.0.3 → 1.0.5

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 CHANGED
@@ -2,16 +2,16 @@
2
2
 
3
3
  ## 1. tweaks和RiffleBridge用法总览
4
4
 
5
- 本文档为Web应用提供Bridge使用指导,以连接外层native应用。分为必选的tweaks和可选的RiffleBridge两个API
5
+ 本文档为当前Web应用提供Bridge使用指导,分为必选的tweaks和可选的RiffleBridge两个API,从而与app应用通信。
6
6
 
7
7
  **安装依赖:**
8
8
  ```bash
9
9
  npm install @ayden-fc2/riffle-bridge-web
10
10
  ```
11
11
 
12
- tweaks用于联通web应用与native的自定义参数,传递支持颜色、数值、文本、布尔、选项类型的JsonSchema格式的tweaksConfig,原则上web应用内的颜色、数值、文本、布尔、选项都要通过tweaks层配置,从而保证native能够响应式控制,使用参考文章第二节。
12
+ tweaks用于联通web应用与app应用的自定义参数,传递支持颜色、数值、文本、布尔、选项类型的JsonSchema格式的tweaksConfig,原则上web应用内的颜色、数值、文本、布尔、选项都要通过tweaks层配置,从而保证app应用能够响应式控制,使用参考文章第二节。
13
13
 
14
- RiffleBridge用于调用震动、音频、相机等模块的native功能,展开对应文章第三节各个模块,根据所需要实现的web应用功能,按需参考模块取用即可;
14
+ RiffleBridge用于调用震动、音频、相机等模块的app应用功能,展开对应文章第三节各个模块,根据所需要实现的web应用功能,按需参考模块取用即可;
15
15
 
16
16
  ## 2. tweaks 用法
17
17
 
@@ -23,20 +23,20 @@ RiffleBridge用于调用震动、音频、相机等模块的native功能,展
23
23
 
24
24
  ```
25
25
  const tweaksConfig = {
26
- // 颜色类型 - Native 端显示颜色选择器
26
+ // 颜色类型 - app应用 端显示颜色选择器
27
27
  color: { name: '颜色', type: 'color', value: '#667eea' },
28
28
 
29
- // 数值类型 - Native 端显示滑块
29
+ // 数值类型 - app应用 端显示滑块
30
30
  size: { name: '大小', type: 'number', value: 150, min: 50, max: 300 },
31
31
  opacity: { name: '透明度', type: 'number', value: 1, min: 0, max: 1, step: 0.1 },
32
32
 
33
- // 文本类型 - Native 端显示文本输入框
33
+ // 文本类型 - app应用 端显示文本输入框
34
34
  title: { name: '标题', type: 'string', value: 'Hello World' },
35
35
 
36
- // 布尔类型 - Native 端显示开关
36
+ // 布尔类型 - app应用 端显示开关
37
37
  enabled: { name: '启用', type: 'boolean', value: true },
38
38
 
39
- // 选项类型 - Native 端显示下拉选择器
39
+ // 选项类型 - app应用 端显示下拉选择器
40
40
  mode: { name: '模式', type: 'select', value: 'normal', options: ['normal', 'fast', 'slow'] },
41
41
  vibration: { name: '震动', type: 'select', value: 'medium', options: [
42
42
  { label: '轻', value: 'light' },
@@ -48,7 +48,7 @@ const tweaksConfig = {
48
48
 
49
49
  ### 2.2 初始化(必须传入React实例)
50
50
 
51
- 通过 { React } 参数显式传入初始化,保证web和native内外层数据联通,响应式更新,初始化代码如下:
51
+ 通过 { React } 参数显式传入初始化,保证web和app应用数据联通,响应式更新,初始化代码如下:
52
52
 
53
53
  ```
54
54
  import React, { useMemo } from 'react';
@@ -64,7 +64,7 @@ const tweaks = useMemo(() => createTweaks(tweaksConfig, { React }), []);
64
64
 
65
65
  ```
66
66
  function Demo() {
67
- // .useState() 自动响应式订阅,Native 修改时自动重渲染
67
+ // .useState() 自动响应式订阅,app应用 修改时自动重渲染
68
68
  const color = tweaks.color.useState();
69
69
  const size = tweaks.size.useState();
70
70
 
@@ -76,7 +76,7 @@ function Demo() {
76
76
 
77
77
  ## 3. RiffleBridge用法
78
78
 
79
- 通过 `RiffleBridge` 类调用 native 功能:
79
+ 通过 `RiffleBridge` 类调用app应用功能:
80
80
 
81
81
  ```
82
82
  import { RiffleBridge } from '@ayden-fc2/riffle-bridge-web';
@@ -112,7 +112,7 @@ await bridge.haptic.intensity(0.5);
112
112
  - **y轴**:正值指向设备顶部
113
113
  - **z轴**:正值指向屏幕外(朝向用户)
114
114
 
115
- > iOS 和 Android 原始坐标系不同,Bridge 已在 Native 层自动处理,WebView 端收到的数据保持一致。
115
+ > iOS 和 Android 原始坐标系不同,Bridge 已在 app应用 层自动处理,WebView 端收到的数据保持一致。
116
116
 
117
117
  ```typescript
118
118
  // 启动传感器
@@ -160,14 +160,14 @@ const tilt = bridge.utils.getTiltDirection(x, y);
160
160
  ```
161
161
 
162
162
  ### 3.3 音频模块(录音、播放)
163
- > 特别注意拿到的uri文件资源是webview外的native文件路径,当前webview无法直接播放音频,请参考录音模块代码,先调用readAsBase64转码再播放!
163
+ > 特别注意拿到的uri文件资源是app应用文件路径,当前web应用无法直接播放音频,请参考录音模块代码,先调用readAsBase64转码再播放!
164
164
 
165
165
  **播放模块:**
166
166
 
167
167
  单例模式实现,主要用于不需要用户交互就直接播放的背景音乐等,点击音效等仍由web应用本身实现
168
168
 
169
169
  ```
170
- // 播放,支持传入 local://xxx 或 file://xxx 的本地路径,或 https://xxx 的url网址
170
+ // 播放,支持传入 local://xxx 或 file://xxx 的app应用本地路径,或 https://xxx 的url网址
171
171
  await bridge.audio.playOnline({ uri: playUri });
172
172
 
173
173
  // 播放并循环(适用于背景音乐)
@@ -208,13 +208,14 @@ console.log('时长:', result.durationMillis / 1000, '秒');
208
208
 
209
209
  // 保存录音(以aydens-voice为例,实际可以替换为web应用的独特键,或者result.uri的独特文件名)
210
210
  if (result?.uri) {
211
+ // 保存文件到app应用应用并记录map
211
212
  await bridge.fileStorage.addCustomFileMapping('local://recording/aydens-voice', result.uri);
212
213
  }
213
214
 
214
- // 1. 后续让native层播放
215
+ // 1. 后续让app应用播放
215
216
  await bridge.audio.playOnline({ uri: 'local://recording/aydens-voice' });
216
217
 
217
- // 2. 后续直接让web播放
218
+ // 2. 后续直接让web应用播放
218
219
  const audioData = await bridge.fileStorage.readAsBase64(result.uri);
219
220
  const audio = new Audio(audioData.dataUrl); // dataUrl 已是完整 'data:audio/mp4;base64,...'
220
221
  audio.play();
@@ -226,7 +227,7 @@ audio.play();
226
227
 
227
228
  ### 3.4 相机模块(实时相机、拍照、相册、闪光灯)
228
229
 
229
- >> 特别注意拿到的uri文件资源是webview外的native文件路径,当前webview无法直接渲染,请参考下方**拍照/相册**的代码,先调用readAsBase64转码再渲染!
230
+ >> 特别注意拿到的uri文件资源是app应用件路径,当前web应用无法直接渲染,请参考下方**拍照/相册**的代码,先调用readAsBase64转码再渲染!
230
231
 
231
232
  **实时相机**
232
233
 
@@ -241,7 +242,9 @@ document.body.style.background = 'transparent';
241
242
  document.documentElement.style.background = 'transparent';
242
243
  document.getElementById('root')!.style.background = 'transparent';
243
244
 
244
- // 拍照 ⚠️ 拍照后不要直接渲染,参考下方**拍照/相册**的代码,先调用readAsBase64转码再渲染!
245
+ // 拍照
246
+ // ⚠️ 拍照后不要直接渲染,参考下方**拍照/相册**的代码,先调用readAsBase64转码再渲染!
247
+ // ⚠️ 拍照后如果需要保存,也参考下方**拍照/相册**的代码调用app应用的文件存储模块保存并记录map
245
248
  const photo = await bridge.camera.takePhoto();
246
249
 
247
250
  // 切换摄像头
@@ -289,13 +292,14 @@ await bridge.camera.setFilter('none');
289
292
  // 1. 拍照(以aydens-demo-photo为例,实际web应用使用picked.uri文件名或组特的key)
290
293
  const photo = await bridge.fileStorage.takePhoto();
291
294
  if (!photo?.cancelled) {
292
- // photo.uri 是沙箱内的本地路径
295
+ // 保存文件到app应用并记录map
293
296
  await bridge.fileStorage.addCustomFileMapping('local://photos/aydens-demo-photo', photo.uri);
294
297
  }
295
298
 
296
299
  // 2. 或从相册选择(以aydens-photo为例,实际web应用使用picked.uri文件名或组特的key)
297
300
  const picked = await bridge.fileStorage.pickFromGallery();
298
301
  if (!picked?.cancelled) {
302
+ // 保存文件到app应用并记录map
299
303
  await bridge.fileStorage.addCustomFileMapping('local://photos/aydens-demo-photo', picked.uri);
300
304
  }
301
305
 
@@ -305,6 +309,43 @@ const base64 = await bridge.fileStorage.readAsBase64('local://photos/aydens-demo
305
309
  <img src={`data:image/jpeg;base64,${base64}`} />
306
310
  ```
307
311
 
312
+ **保存到系统相册**
313
+
314
+ 将图片或视频保存到用户的系统相册(会请求系统相册写入权限)
315
+
316
+ ```
317
+ // 拍照后保存到系统相册
318
+ const photo = await bridge.fileStorage.takePhoto();
319
+ if (!photo?.cancelled) {
320
+ // 保存到系统相册(默认相册名 'Riffle')
321
+ const result = await bridge.fileStorage.saveToGallery(photo.uri);
322
+ console.log('保存成功:', result.filename);
323
+
324
+ // 或指定自定义相册名
325
+ const result2 = await bridge.fileStorage.saveToGallery(photo.uri, 'MyApp相册');
326
+ }
327
+
328
+ // 也可以保存已缓存的文件到系统相册
329
+ const cached = await bridge.fileStorage.getLocalFileByUrl('local://photos/aydens-demo-photo');
330
+ if (cached?.exists && cached.uri) {
331
+ await bridge.fileStorage.saveToGallery(cached.uri);
332
+ }
333
+ ```
334
+
335
+ 返回值类型:
336
+ ```typescript
337
+ interface SaveToGalleryResult {
338
+ success: boolean; // 是否成功
339
+ assetId?: string; // 系统相册资产ID
340
+ uri?: string; // 保存后的URI
341
+ filename?: string; // 文件名
342
+ mediaType?: string; // 媒体类型 'photo' | 'video'
343
+ width?: number; // 图片宽度
344
+ height?: number; // 图片高度
345
+ duration?: number; // 视频时长(秒)
346
+ }
347
+ ```
348
+
308
349
  **闪光灯**
309
350
 
310
351
  闪光灯必须打开实时相机才能调用,参考实时相机的说明。
@@ -312,7 +353,7 @@ const base64 = await bridge.fileStorage.readAsBase64('local://photos/aydens-demo
312
353
  当应用需要闪光灯而不要实时相机时,可以使用非透明web应用背景来遮挡实时相机
313
354
 
314
355
  ### 3.5 文件存储模块
315
- >> 特别注意拿到的uri文件资源是webview外的native文件路径,当前webview无法直接渲染图片或播放音频,请参考下方代码,先调用readAsBase64转码再渲染或播放!相机&录音等模块也都有联动说明
356
+ >> 特别注意拿到的uri文件资源是app应用文件路径,当前web应用无法直接渲染图片或播放音频,请参考下方代码,先调用readAsBase64转码再渲染或播放!相机&录音等模块也都有联动说明
316
357
 
317
358
  文件映射系统维护了一个MAP映射表,支持本地文件和网络资源两种存储,web应用要根据需求选择使用哪种
318
359
 
@@ -328,7 +369,7 @@ if (!photo?.cancelled) {
328
369
  // 查询(查看是否存在)
329
370
  const cached = await bridge.fileStorage.getLocalFileByUrl('local://avatar/123');
330
371
 
331
- // 拿到base64(真正供web使用)
372
+ // 拿到base64
332
373
  const base64 = await bridge.fileStorage.readAsBase64('local://avatar/123');
333
374
  // 在 Web 中渲染
334
375
  <img src={`data:image/jpeg;base64,${base64}`} />
@@ -345,6 +386,11 @@ console.log(result.cached ? '使用缓存' : '已下载', result.uri);
345
386
  const base64 = await bridge.fileStorage.readAsBase64(url);
346
387
  ```
347
388
 
389
+ **读取资源并解码base64,从app应用传入web应用**
390
+ ```
391
+ // uri为mapRecord记录值
392
+ const base64 = await bridge.fileStorage.readAsBase64(mapRecord);
393
+ ```
348
394
 
349
395
 
350
396
  ### 3.6 设备信息模块
package/dist/index.d.mts CHANGED
@@ -405,6 +405,21 @@ declare class FileStorageController {
405
405
  added: boolean;
406
406
  }>;
407
407
  readAsBase64(uriOrKey: string): Promise<Base64Result>;
408
+ /**
409
+ * 保存图片/视频到系统相册
410
+ * @param uri 文件 URI(支持 file:// 或 local:// 路径)
411
+ * @param albumName 相册名称(默认 'AIGames')
412
+ */
413
+ saveToGallery(uri: string, albumName?: string): Promise<{
414
+ success: boolean;
415
+ assetId?: string;
416
+ uri?: string;
417
+ filename?: string;
418
+ mediaType?: string;
419
+ width?: number;
420
+ height?: number;
421
+ duration?: number;
422
+ }>;
408
423
  }
409
424
  interface PlayOptions {
410
425
  uri: string;
package/dist/index.d.ts CHANGED
@@ -405,6 +405,21 @@ declare class FileStorageController {
405
405
  added: boolean;
406
406
  }>;
407
407
  readAsBase64(uriOrKey: string): Promise<Base64Result>;
408
+ /**
409
+ * 保存图片/视频到系统相册
410
+ * @param uri 文件 URI(支持 file:// 或 local:// 路径)
411
+ * @param albumName 相册名称(默认 'AIGames')
412
+ */
413
+ saveToGallery(uri: string, albumName?: string): Promise<{
414
+ success: boolean;
415
+ assetId?: string;
416
+ uri?: string;
417
+ filename?: string;
418
+ mediaType?: string;
419
+ width?: number;
420
+ height?: number;
421
+ duration?: number;
422
+ }>;
408
423
  }
409
424
  interface PlayOptions {
410
425
  uri: string;
package/dist/index.js CHANGED
@@ -419,6 +419,17 @@ var FileStorageController = class {
419
419
  async readAsBase64(uriOrKey) {
420
420
  return this.core.send("fileStorage", "readAsBase64", { uriOrKey });
421
421
  }
422
+ /**
423
+ * 保存图片/视频到系统相册
424
+ * @param uri 文件 URI(支持 file:// 或 local:// 路径)
425
+ * @param albumName 相册名称(默认 'AIGames')
426
+ */
427
+ async saveToGallery(uri, albumName = "Riffle") {
428
+ return this.core.send("fileStorage", "saveToGallery", {
429
+ saveUri: uri,
430
+ albumName
431
+ });
432
+ }
422
433
  };
423
434
  var AudioController = class {
424
435
  constructor(core, fileStorage) {
package/dist/index.mjs CHANGED
@@ -377,6 +377,17 @@ var FileStorageController = class {
377
377
  async readAsBase64(uriOrKey) {
378
378
  return this.core.send("fileStorage", "readAsBase64", { uriOrKey });
379
379
  }
380
+ /**
381
+ * 保存图片/视频到系统相册
382
+ * @param uri 文件 URI(支持 file:// 或 local:// 路径)
383
+ * @param albumName 相册名称(默认 'AIGames')
384
+ */
385
+ async saveToGallery(uri, albumName = "Riffle") {
386
+ return this.core.send("fileStorage", "saveToGallery", {
387
+ saveUri: uri,
388
+ albumName
389
+ });
390
+ }
380
391
  };
381
392
  var AudioController = class {
382
393
  constructor(core, fileStorage) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ayden-fc2/riffle-bridge-web",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Riffle Bridge Web SDK - WebView 与 Native 通信桥接库",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -301,6 +301,27 @@ export class FileStorageController {
301
301
  async readAsBase64(uriOrKey: string): Promise<Base64Result> {
302
302
  return this.core.send<Base64Result>('fileStorage', 'readAsBase64', { uriOrKey });
303
303
  }
304
+
305
+ /**
306
+ * 保存图片/视频到系统相册
307
+ * @param uri 文件 URI(支持 file:// 或 local:// 路径)
308
+ * @param albumName 相册名称(默认 'AIGames')
309
+ */
310
+ async saveToGallery(uri: string, albumName = 'Riffle'): Promise<{
311
+ success: boolean;
312
+ assetId?: string;
313
+ uri?: string;
314
+ filename?: string;
315
+ mediaType?: string;
316
+ width?: number;
317
+ height?: number;
318
+ duration?: number;
319
+ }> {
320
+ return this.core.send('fileStorage', 'saveToGallery', {
321
+ saveUri: uri,
322
+ albumName,
323
+ });
324
+ }
304
325
  }
305
326
 
306
327
  // ============================================