@netless/appliance-plugin 1.1.30-beta.1 → 1.1.31

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/READMA.zh-CN.md CHANGED
@@ -1,16 +1,43 @@
1
1
  # appliance-plugin
2
2
 
3
- 该插件基于 white-web-sdk 的插件机制,实现了一套白板教具的绘制工具。同时也基于 @netless/window-manager,实现了可在多窗口上使用。
3
+ [English Documentation](README.md)
4
+
5
+ 该插件基于 white-web-sdk 的插件机制,实现了一套功能丰富的白板教具绘制工具。同时也基于 @netless/window-manager,实现了可在多窗口上使用。
4
6
 
5
7
  ## 简介
6
8
 
7
- appliance-plugin 依赖 [white-web-sdk](https://www.npmjs.com/package/white-web-sdk) 和 [@netless/window-manager](https://www.npmjs.com/package/@netless/window-manager),并基于 Web API 对 [OffscreenCanvas](https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas) 的支持。
9
+ appliance-plugin 是一个高性能的白板绘制插件,依赖 [white-web-sdk](https://www.npmjs.com/package/white-web-sdk) 和 [@netless/window-manager](https://www.npmjs.com/package/@netless/window-manager),并基于 Web API 对 [OffscreenCanvas](https://developer.mozilla.org/zh-CN/docs/Web/API/OffscreenCanvas) 的支持。
10
+
11
+ ### 主要特性
12
+
13
+ - 🎨 **丰富的绘制工具**:支持铅笔、橡皮擦、形状工具、文字、图片等多种绘制工具
14
+ - ⚡ **高性能渲染**:采用双 WebWorker + OffscreenCanvas 机制,绘制效率比主线程提升 40% 以上
15
+ - 🖼️ **多窗口支持**:支持多窗口场景,可在不同窗口上独立使用
16
+ - 🎯 **激光笔工具**:支持激光笔功能,适合演示场景
17
+ - 📝 **文字编辑**:支持文字插入、编辑、样式设置等功能
18
+ - 🗺️ **小地图功能**:提供小地图导航功能,方便查看整体内容
19
+ - 🔄 **撤销重做**:支持全局撤销重做功能
20
+ - 🎭 **自定义样式**:支持自定义画笔样式、文字样式等
21
+ - 🔌 **插件扩展**:支持通过插件机制扩展功能(如 autoDraw 手写图形自动联想)
8
22
 
9
23
  ## 原理
10
24
 
11
- 1. 该插件主要是基于 SpriteJS 的 2D 功能,支持 WebGL2 渲染,并可向后兼容降级为 WebGL 和 Canvas2D。
12
- 2. 该插件通过双 WebWorker + OffscreenCanvas 机制,把绘制计算和渲染逻辑都放在独立的 worker 线程中处理,不占用主线程的 CPU 任务。
13
- 3. 针对移动端有些终端不支持 OffscreenCanvas,则会把它放在主线程处理。
25
+ 1. **渲染引擎**:该插件主要是基于 SpriteJS 的 2D 功能,支持 WebGL2 渲染,并可向后兼容降级为 WebGL 和 Canvas2D。
26
+ 2. **多线程架构**:该插件通过双 WebWorker + OffscreenCanvas 机制,把绘制计算和渲染逻辑都放在独立的 worker 线程中处理,不占用主线程的 CPU 任务。
27
+ - **Full Worker**:负责绘制完整数据的线程
28
+ - **Sub Worker**:负责绘制一帧数据的线程
29
+ 3. **兼容性处理**:针对移动端有些终端不支持 OffscreenCanvas,则会自动降级到主线程处理。
30
+
31
+ ### 支持的绘制工具
32
+
33
+ 插件支持以下绘制工具:
34
+
35
+ - **基础工具**:铅笔、橡皮擦、局部橡皮擦、位图橡皮擦、选择工具、抓手工具
36
+ - **形状工具**:直线、箭头、矩形、圆形、三角形、菱形、多边形、星形、聊天泡泡框
37
+ - **文字工具**:支持文字输入、编辑、样式设置
38
+ - **图片工具**:支持图片插入和编辑
39
+ - **特殊工具**:激光笔、背景 SVG
40
+ - **互动工具**:点击互动工具(可供插件自定义行为)
14
41
 
15
42
  ## 插件用法
16
43
 
@@ -36,7 +63,7 @@ import { ApplianceSinglePlugin } from '@netless/appliance-plugin';
36
63
  >
37
64
  > 我们采用双 worker 并发来提高绘制效率,这样让它比主线程效率提高了 40% 以上。但是两个 worker 文件上的公共依赖都是重复的,所以如果直接构建到包中,那么会大大增加包体积。所以我们允许 worker.js 文件 CDN 部署,只要把 `@netless/appliance-plugin/cdn` 下的文件部署到 CDN 中即可,然后通过插件中的 `getInstance` 的第二个参数 `options.cdn` 中配置上两个 worker.js 的 CDN 地址即可。这样就可以解决包体积过大的问题。
38
65
  >
39
- > **总包大概在 400kB,两个 worker.js 各有 800kB。** 如果需要考虑构建的包体积大小的,请选择配置 CDN。
66
+ > 如果需要考虑构建的包体积大小的,请选择配置 CDN。
40
67
 
41
68
  ### 接入方式参考
42
69
 
@@ -220,11 +247,15 @@ injectMethodToObject(windowmanager,'cleanCurrentScene');
220
247
  injectMethodToObject(windowmanager,'insertImage');
221
248
  injectMethodToObject(windowmanager,'completeImageUpload');
222
249
  injectMethodToObject(windowmanager,'lockImage');
250
+ injectMethodToObject(windowmanager, "insertText");
251
+ injectMethodToObject(windowmanager, "updateText");
223
252
  injectMethodToObject(room,'getImagesInformation');
224
253
  injectMethodToObject(room,'callbacks');
225
254
  injectMethodToObject(room,'screenshotToCanvasAsync');
226
255
  injectMethodToObject(room,'getBoundingRectAsync');
227
256
  injectMethodToObject(room,'scenePreviewAsync');
257
+ injectMethodToObject(room, "fillSceneSnapshotAsync");
258
+ injectMethodToObject(room, "setWritable");
228
259
  injectMethodToObject(windowmanager.mainView,'setMemberState');
229
260
  // 这些我们可以通过前端日志看到调用行为,例如:
230
261
  // [ApplianceMultiPlugin] setMemberState
@@ -278,6 +309,9 @@ injectMethodToObject(windowmanager.mainView,'setMemberState');
278
309
  - `getElements` - 获取场景下的所有元素 (Version >=1.1.19)
279
310
  - `stopDraw` - 停止Draw事件 (Version >=1.1.19)
280
311
  - `setViewLocalScenePathChange` - 设置白板本地场景路径变化 (Version >=1.1.27)
312
+ - `insertMarkmap` - 插入markdow文本到白板 (Version >=1.1.30) **该方法需要开启extras.useBackgroundThread**
313
+ - `updateMarkmap` - 修改白板中的markdow文本 (Version >=1.1.30) **该方法需要开启extras.useBackgroundThread**
314
+ - `insertBackgroundImage` - 插入白板的背景图片 (Version >=1.1.30) **该方法需要开启extras.useBackgroundThread**
281
315
 
282
316
  5. 不兼容接口
283
317
  - [`exportScene`](https://doc.shengwang.cn/api-ref/whiteboard/javascript/interfaces/room#exportScene) - appliance-plugin 开启后,笔记不能按 room 的方式导出
@@ -604,6 +638,75 @@ plugin.usePlugin(autoDrawPlugin);
604
638
  ```
605
639
  ![Image](https://github.com/user-attachments/assets/c388691c-ae72-44ec-bbb7-e92c3a73c9c7)
606
640
 
641
+ ##### 插入思维导图(需要markdown文本)
642
+ ```ts
643
+ import { ApplianceMultiPlugin } from '@netless/appliance-plugin';
644
+ const plugin = await ApplianceMultiPlugin.getInstance(manager, {
645
+ options: {
646
+ cdn: {...}
647
+ extras: {
648
+ ...,
649
+ useBackgroundThread: true,
650
+ }
651
+ },
652
+ });
653
+ const markId = await plugin.insertMarkmap(viewId, {
654
+ data: `# 一集标题
655
+ ## 二级标题1
656
+ ### 三级标题1
657
+ ### 三级标题2
658
+ #### 四级标题1
659
+ #### 四级标题2
660
+ #### 四级标题3
661
+ ## 二级标题2
662
+ ### 三级标题1
663
+ ### 三级标题2`,
664
+ uuid: '唯一标识',
665
+ centerX: 0,
666
+ centerY: 0,
667
+ width: 200,
668
+ height: 200,
669
+ locked: false,
670
+ });
671
+ plugin.updateMarkmap(viewId, markId, {
672
+ data: `# 一集标题
673
+ ## 二级标题1
674
+ ## 二级标题2
675
+ ### 三级标题1
676
+ ### 三级标题2`,
677
+ uuid: '唯一标识',
678
+ centerX: 0,
679
+ centerY: 0,
680
+ width: 200,
681
+ height: 200,
682
+ locked: false,
683
+ } )
684
+
685
+ ```
686
+ ![Image](https://github.com/user-attachments/assets/0d278bd5-1cc7-413f-881c-8a43ef1429e3)
687
+ ##### 插入背景图
688
+ ```ts
689
+ import { ApplianceMultiPlugin } from '@netless/appliance-plugin';
690
+ const plugin = await ApplianceMultiPlugin.getInstance(manager, {
691
+ options: {
692
+ cdn: {...}
693
+ extras: {
694
+ ...,
695
+ useBackgroundThread: true,
696
+ }
697
+ },
698
+ });
699
+ plugin.insertBackgroundImage(viewId, {
700
+ src: 'https://example.com/background.png'
701
+ uuid: '唯一标识',
702
+ centerX: 0,
703
+ centerY: 0,
704
+ width: 200,
705
+ height: 200,
706
+ locked: true,
707
+ })
708
+ ```
709
+
607
710
  ### 配置参数
608
711
  `getInstance(wm: WindowManager | Room | Player, adaptor: ApplianceAdaptor)`
609
712
  - `wm`: `WindowManager | Room | Player`。多窗口模式下传入的是 `WindowManager`,单窗口模式下传入的是 `Room` 或者 `Player`(白板回放模式)。
@@ -628,12 +731,17 @@ plugin.usePlugin(autoDrawPlugin);
628
731
  1、绘制将使用单worker绘制,画笔过程中无法使用贝塞尔圆滑处理。
629
732
  2、移除部分新功能:小地图、pointerPen(激光笔)、autoDraw插件。
630
733
  */
631
- useSimple: boolean;
734
+ useSimple?: boolean;
632
735
  /** 是否使用 worker, 默认值为 ``auto``
633
736
  * auto: 自动选择(如果浏览器支持 offscreenCanvas 则使用 webWorker, 否则使用主线程)
634
737
  * mainThread: 使用主线程, canvas 绘制数据。
635
738
  */
636
739
  useWorker?: UseWorkerType;
740
+ /** 是否使用 backgroundThread, 默认值为 ``false``
741
+ * true: 使用 backgroundThread, 可以调用 ``insertMarkmap``, ``updateMarkmap``, ``insertBackgroundImage``
742
+ * false: 不使用 backgroundThread
743
+ */
744
+ useBackgroundThread?: boolean;
637
745
  /** 同步数据配置项 */
638
746
  syncOpt?: SyncOpt;
639
747
  /** 画布配置项 */
@@ -664,9 +772,170 @@ plugin.usePlugin(autoDrawPlugin);
664
772
  > 如需要上传到白板日志服务器,可以把 `room.logger` 配置到该项目。
665
773
 
666
774
  ### 前端调试
775
+
667
776
  对接过程中如果想了解和跟踪插件内部状态,可以通过以下几个控制台指令,查看内部数据。
777
+
668
778
  ```js
669
779
  const appliancePlugin = await ApplianceSinglePlugin.getInstance(...)
670
780
  appliancePlugin.currentManager // 可以查看到包版本号,内部状态等
671
781
  appliancePlugin.currentManager.consoleWorkerInfo() // 可以查看到 worker 上的绘制信息
672
782
  ```
783
+
784
+ ## 使用示例
785
+
786
+ ### 基础使用示例
787
+
788
+ ```js
789
+ import { ApplianceSinglePlugin } from '@netless/appliance-plugin';
790
+ import '@netless/appliance-plugin/dist/style.css';
791
+
792
+ // 方式1: 使用 CDN(推荐生产环境)
793
+ const plugin = await ApplianceSinglePlugin.getInstance(room, {
794
+ options: {
795
+ cdn: {
796
+ fullWorkerUrl: 'https://your-cdn.com/fullWorker.js',
797
+ subWorkerUrl: 'https://your-cdn.com/subWorker.js',
798
+ },
799
+ },
800
+ });
801
+
802
+ // 方式2: 使用本地 worker 文件(适合开发环境)
803
+ import fullWorkerString from '@netless/appliance-plugin/dist/fullWorker.js?raw';
804
+ import subWorkerString from '@netless/appliance-plugin/dist/subWorker.js?raw';
805
+ const fullWorkerBlob = new Blob([fullWorkerString], {type: 'text/javascript'});
806
+ const fullWorkerUrl = URL.createObjectURL(fullWorkerBlob);
807
+ const subWorkerBlob = new Blob([subWorkerString], {type: 'text/javascript'});
808
+ const subWorkerUrl = URL.createObjectURL(subWorkerBlob);
809
+
810
+ const plugin = await ApplianceSinglePlugin.getInstance(room, {
811
+ options: {
812
+ cdn: {
813
+ fullWorkerUrl,
814
+ subWorkerUrl,
815
+ },
816
+ },
817
+ });
818
+ ```
819
+
820
+ ### 切换绘制工具
821
+
822
+ ```js
823
+ import { ApplianceNames, EStrokeType } from '@netless/appliance-plugin';
824
+
825
+ // 切换到铅笔工具
826
+ room.setMemberState({ currentApplianceName: ApplianceNames.pencil });
827
+
828
+ // 切换到矩形工具
829
+ room.setMemberState({ currentApplianceName: ApplianceNames.rectangle });
830
+
831
+ // 切换到激光笔工具
832
+ room.setMemberState({
833
+ currentApplianceName: ApplianceNames.laserPen,
834
+ strokeType: EStrokeType.Normal
835
+ });
836
+
837
+ // 切换到文字工具
838
+ room.setMemberState({ currentApplianceName: ApplianceNames.text });
839
+ ```
840
+
841
+ ### 自定义样式示例
842
+
843
+ ```js
844
+ // 设置画笔样式为虚线
845
+ room.setMemberState({
846
+ strokeType: EStrokeType.Dotted,
847
+ strokeOpacity: 0.8
848
+ });
849
+
850
+ // 设置图形填充
851
+ room.setMemberState({
852
+ fillColor: [255, 0, 0], // 红色
853
+ fillOpacity: 0.5
854
+ });
855
+
856
+ // 设置文字样式
857
+ room.setMemberState({
858
+ textOpacity: 0.9,
859
+ textBgColor: [255, 255, 0], // 黄色背景
860
+ textBgOpacity: 0.3
861
+ });
862
+ ```
863
+
864
+ ### 文字编辑示例
865
+
866
+ ```js
867
+ // 在指定位置插入文字
868
+ const textId = plugin.insertText(100, 100, 'Hello World');
869
+
870
+ // 编辑文字内容
871
+ plugin.updateText(textId, 'Updated Text');
872
+
873
+ // 移除文字焦点
874
+ plugin.blurText();
875
+ ```
876
+
877
+ ### 小地图功能示例
878
+
879
+ ```js
880
+ // 创建小地图
881
+ const minimapDiv = document.getElementById('minimap');
882
+ await plugin.createMiniMap('mainView', minimapDiv);
883
+
884
+ // 销毁小地图
885
+ await plugin.destroyMiniMap('mainView');
886
+ ```
887
+
888
+ ### 撤销重做示例
889
+
890
+ ```js
891
+ // 撤销操作
892
+ const undoSteps = plugin.undo();
893
+
894
+ // 重做操作
895
+ const redoSteps = plugin.redo();
896
+
897
+ // 检查是否可以撤销/重做
898
+ const canUndo = plugin.canUndoSteps() > 0;
899
+ const canRedo = plugin.canRedoSteps() > 0;
900
+ ```
901
+
902
+ ## 常见问题
903
+
904
+ ### 1. 如何选择合适的接入方式?
905
+
906
+ - **fastboard**:如果你使用的是 fastboard 框架,推荐使用 fastboard 的集成方式,配置最简单
907
+ - **多窗口场景**:如果需要多窗口功能,使用 `ApplianceMultiPlugin`
908
+ - **单白板场景**:如果只需要单白板功能,使用 `ApplianceSinglePlugin`
909
+
910
+ ### 2. Worker 文件部署方式选择?
911
+
912
+ - **CDN 部署**(推荐):适合生产环境,可以减少主包体积(主包约 400kB,两个 worker 各约 800kB)
913
+ - **本地打包**:适合开发环境或对包体积不敏感的场景
914
+
915
+ ### 3. 性能优化建议
916
+
917
+ - 使用 CDN 部署 worker 文件,减少主包体积
918
+ - 合理配置 `bufferSize`,根据设备性能调整画布缓存大小
919
+ - 在移动端或低性能设备上,可以考虑使用 `useSimple: true` 简单模式
920
+ - 如有非必要的功能, 可以不用开启 `useBackgroundThread: true`
921
+
922
+ ### 4. 兼容性说明
923
+
924
+ - 支持现代浏览器(Chrome、Firefox、Safari、Edge)
925
+ - 移动端浏览器支持情况取决于 OffscreenCanvas 支持情况
926
+ - 不支持 OffscreenCanvas 的设备会自动降级到主线程模式
927
+
928
+ ## 版本历史
929
+
930
+ 详细的版本更新记录请查看 [CHANGELOG.md](./CHANGELOG.md)
931
+
932
+ ## 许可证
933
+
934
+ MIT License
935
+
936
+ ## 相关链接
937
+
938
+ - [white-web-sdk](https://www.npmjs.com/package/white-web-sdk)
939
+ - [@netless/window-manager](https://www.npmjs.com/package/@netless/window-manager)
940
+ - [fastboard](https://github.com/netless-io/fastboard)
941
+ - [官方文档](https://doc.shengwang.cn/)
package/cdn/cdn.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r="/fullWorker-p8falm.js",e="/subWorker-C9RT-E.js";exports.fullWorkerUrl=r;exports.subWorkerUrl=e;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r="/fullWorker-8cQCRH.js",e="/subWorker-Bj1Oy7.js";exports.fullWorkerUrl=r;exports.subWorkerUrl=e;