jvs-draw
v1.1.23
Published
`jvs-draw` 是一个基于 Vue 3 和 Element Plus 开发的轻量级虚拟白板(Virtual Whiteboard)组件。它可以轻松集成到您的 Vue 3 项目中,提供流程图绘制、草图绘制和自由书画等功能。
Downloads
153
Readme
jvs-draw
jvs-draw 是一个基于 Vue 3 和 Element Plus 开发的轻量级虚拟白板(Virtual Whiteboard)组件。它可以轻松集成到您的 Vue 3 项目中,提供流程图绘制、草图绘制和自由书画等功能。
📦 安装
首先,在您的项目中安装 jvs-draw 以及它的依赖。由于 jvs-draw 具有一些同行依赖 (peerDependencies),您还需要确保安装了 vue, pinia 和 element-plus。
npm install jvs-draw
# 或使用 yarn
yarn add jvs-draw
# 或使用 pnpm
pnpm add jvs-draw如果您尚未安装必需的同行依赖和样式库,请同时安装它们:
npm install vue pinia element-plus jvs-picker-color-v3 remixicon🚀 快速上手
1. 引入样式
在项目的全局入口文件(通常是 main.ts 或 main.js)中引入必需的样式文件以及注册相关依赖。
// main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
// 引入 Element Plus
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
// 引入相关外部依赖样式(图标和颜色选择器等)
import 'remixicon/fonts/remixicon.css';
import jvsPickerColorV3 from 'jvs-picker-color-v3';
import 'jvs-picker-color-v3/lib/jvs-picker-color-v3.css';
// 引入 jvs-draw 的核心样式和自建字体库 CSS
import 'jvs-draw/jvs-draw.css';
const app = createApp(App);
app.use(createPinia()); // jvs-draw 依赖 Pinia 进行状态管理
app.use(ElementPlus);
app.use(jvsPickerColorV3);
app.mount('#app');[!IMPORTANT] 关于自定义 SVG 图标库导入说明:
组件包默认已将 CSS 字体 (
iconfont.css) 打包到jvs-draw.css中。但如果你项目中使用了需要SVG多色支持的特性 (如内置的高级图形菜单图标),你需要引入对应的iconfont.js。 组件发行包 (dist) 内部包含了这些文件,你需要手动在你的项目的index.html中引入这两个脚本,或者将它们放到你项目的public目录下:<script src="/[你的静态目录]/icon-fonts/iconfont.js"></script> <script src="/[你的静态目录]/public-fonts/iconfont.js"></script>
2. 作为全局组件使用 (可选)
您可以在 main.ts 中全局注册它:
import { JvsDraw } from 'jvs-draw';
app.use(JvsDraw);全局注册后,就可以在任何地方直接使用 <JvsDraw /> 标签。
3. 作为局部组件使用
在您的 Vue 组件(比如 App.vue 或其他视图)中引入并使用该组件。由于该组件是一个完整的白板画布,推荐将容器高度和宽度设置为 100% 或 100vh/100vw。
<template>
<div class="whiteboard-container">
<JvsDraw />
</div>
</template>
<script setup lang="ts">
import { JvsDraw } from 'jvs-draw';
</script>
<style scoped>
.whiteboard-container {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden; /* 防止页面出现原生滚动条 */
}
</style>🛠️ API & 组件通信
JvsDraw 内部已经集成了完整的状态管理(基于 Pinia),画笔工具、撤销/重做、画布缩放及平移等均在组件内部完成闭环。
如果您想在外部获取或设置画布数据,您可以通过在 <JvsDraw /> 上绑定 ref 来调用组件对外暴露的方法:
<template>
<div class="whiteboard-container">
<JvsDraw ref="drawRef" :initialData="initialData" :loadFromLocal="false" />
<button @click="handleGetData">获取数据</button>
<button @click="handleSetData">应用数据</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { JvsDraw } from 'jvs-draw';
const drawRef = ref();
const initialData = { /* 画板初始数据 */ };
// 获取画板数据
const handleGetData = () => {
if (drawRef.value) {
const data = drawRef.value.getCanvasData();
console.log("当前画布数据:", data.elements);
console.log("当前画布状态:", data.appState);
}
};
// 动态设置画板数据
const handleSetData = () => {
if (drawRef.value) {
const newData = { /* 新的画板数据 */ };
drawRef.value.setCanvasData(newData);
}
};
// 配置画板 (如: 开启并自定义图片上传)
const handleSetting = () => {
if (drawRef.value) {
drawRef.value.setConfig({
enableImageUpload: true, // 开启自定义图片上传
uploadImageFn: async (file: File) => {
// 在这里对接你自己的后端上传接口
const formData = new FormData();
formData.append('file', file);
// 假设的上传请求
// const res = await axios.post('/api/upload', formData);
// return res.data.url;
// 此处返回模拟的线上图片地址作为示例
return URL.createObjectURL(file);
}
});
}
};
</script>(注意:所有方法均需通过 ref 获取组件实例后调用,以支持多实例独立运行。)
4. 完整的可选配置项 (Config)
通过组件暴露的 setConfig 方法,您可以传入以下参数来全局定制画板的 UI 显示及其他核心设置:
| 配置项 | 类型 | 默认值 | 说明 |
| :--- | :---: | :---: | :--- |
| showToolbar | boolean | true | 是否显示左侧工具栏 (包含画笔、形状工具等) |
| showPropertiesPanel | boolean | true | 是否显示右侧属性面板 (用于修改颜色、线宽等) |
| showBoardName | boolean | true | 是否显示在左上角的画板名称标题 |
| showFooter | boolean | true | 是否显示底部栏 (包含缩放、拖拽视口工具等) |
| editable | boolean | true | 是否开启编辑模式。设为 false 画板进入纯浏览/只读模式 |
| loadFromLocal | boolean | true | 初始化时是否优先从本地缓存读取画板数据(而非使用 initialData) |
| initialData | Object | undefined | 初始画布数据对象。包含 elements (数组) 和 appState (对象) |
| enableImageUpload | boolean | false | 是否开启自定义图片上传 |
| uploadImageFn | Function | undefined | 开启图片上传时的实际拦截函数 (file: File) => Promise<string> |
| language | string | 'zh-CN' | 设置当前显示的语言,内置 'zh-CN' 和 'en-US' |
| showLanguageMenu | boolean | true | 是否在左侧菜单显示语言切换选项 |
| languageList | Array | undefined | 自定义语言下拉列表选项结构:[{ label: 'x', value: 'x' }],传入后自动追加 |
| messages | Object | undefined | 自定义外部语言的翻译字典集,结构为 Record<string, Record<string, string>> |
5. 自定义国际化语言 (i18n)
jvs-draw 内置了简体中文 (zh-CN) 和英文 (en-US)。如果需要引入其他语言并扩充下拉切换列表,可以通过传入 languageList 和 messages 来动态拓展。
例如,增加繁体中文 (zh-TW) 支持:
import { setConfig } from 'jvs-draw';
setConfig({
showLanguageMenu: true, // 确保显示语言切换菜单
languageList: [
{ label: '繁体中文', value: 'zh-TW' } // 新选项会自动追加在这列表里
],
messages: {
'zh-TW': {
'board.language': '語言',
'board.backgroundGrid': '背景網格',
// ... 其它你在界面上见到的相关词条都可以在此传入重写
}
}
});🧩 依赖说明
vue>= 3.xpinia: 状态管理element-plus: UI 组件库roughjs: 核心底层图形手绘风格渲染库jvs-picker-color-v3: 内部依赖的颜色取色器remixicon: 图标库
🔄 协同编辑
jvs-draw 内置了协同操作协议,支持通过 WebSocket 实现多用户实时协同编辑。使用方只需配置两个回调即可接入。
接入方式
方式一:通过 ref 调用(推荐)
<template>
<div class="whiteboard-container">
<JvsDraw ref="drawRef" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { JvsDraw, setConfig } from 'jvs-draw';
const drawRef = ref();
const ws = new WebSocket('ws://your-server/collab');
// 1. 本地操作变更时,通过 WS 广播给其他用户
setConfig({
onCollabAction: (action) => {
ws.send(JSON.stringify(action));
},
});
// 2. 收到远端消息时,调用 applyCollabAction 更新画布
ws.onmessage = (msg) => {
const action = JSON.parse(msg.data);
drawRef.value?.applyCollabAction(action);
};
</script>方式二:通过 globalConfig 调用
import { setConfig, globalConfig } from 'jvs-draw';
const ws = new WebSocket('ws://your-server/collab');
// 本地变更 → WS 广播
setConfig({
onCollabAction: (action) => {
ws.send(JSON.stringify(action));
},
});
// WS 接收 → 应用到画布(applyRemoteAction 在组件 mount 后自动绑定)
ws.onmessage = (msg) => {
globalConfig.applyRemoteAction!(JSON.parse(msg.data));
};数据流向
用户A 操作画布
→ store 内部触发 broadcast()
→ onCollabAction(action) 回调
→ 使用方通过 WS 发送给服务端
→ 服务端广播给用户B
→ 用户B 的 ws.onmessage 收到
→ 调用 applyCollabAction(action) 或 globalConfig.applyRemoteAction(action)
→ 内部自动应用到 store,画布重绘操作类型
所有操作格式为 { type: string, payload: any },共 16 种:
| type | 说明 | payload 关键字段 |
| :--- | :--- | :--- |
| element:add | 新增元素 | element |
| element:addImage | 新增图片 | element, imageData |
| element:update | 更新属性(移动/缩放/旋转/样式等) | id, updates |
| element:move | 批量移动 | ids, deltaX, deltaY |
| element:delete | 删除元素 | id |
| element:deleteBatch | 批量删除 | ids |
| elements:updateSelected | 批量更新选中元素属性 | selectedIds, updates |
| elements:setOrder | 层级调整 | orderedIds |
| elements:paste | 粘贴/创建副本(原子操作) | elements, images? |
| elements:group | 分组 | ids, groupId |
| elements:ungroup | 取消分组 | ids |
| elements:align | 对齐 | alignment, updates |
| elements:distribute | 均匀分布 | direction, updates |
| elements:clear | 清空画布 | 无 |
| image:updateSource | 替换图片 | elementId, fileId, imageData, width, height |
| canvas:replaceAll | 全量替换(初次同步/冲突恢复) | elements, images? |
不需要广播的操作
以下操作仅影响当前用户视图,不会触发 onCollabAction:
选中/取消选中、滚动/缩放、切换工具、网格显示、锚点编辑模式、撤销/重做、复制到剪贴板。
高频操作优化建议
element:update 和 element:move 在拖拽时每帧触发,建议 WS 层做节流(~50ms),或在 pointerup 时一次性发送最终位置。
扩展:光标/选区可视化
如需显示其他用户的光标和选区,可额外定义 presence 消息(使用方自行实现,不走 onCollabAction):
{ "type": "presence:cursor", "payload": { "userId": "u1", "x": 500, "y": 300, "color": "#ff6600" } }
{ "type": "presence:selection", "payload": { "userId": "u1", "ids": ["abc123"], "color": "#ff6600" } }