# montage **Repository Path**: xsw1999/montage ## Basic Information - **Project Name**: montage - **Description**: 一个基于canvas的绘图库 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 0 - **Created**: 2023-01-03 - **Last Updated**: 2025-08-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: Canvas, 绘图库 ## README # montage.js ## 简介 > montage.js 是一个基于 canvas 的绘图库,并且内置了一些鼠标交互功能,在一些需要用到 canvas 的场景下可使用此库来更简单的完成开发,比如拼接图片、简易绘图功能等等 --- > - 开发此插件的初衷是 “既能满足前端基本的绘图功能,又能满足二次开发的需求”,如果需要二次开发,则可以通过节点暴露出来的变量做任何想做的事情 > - 所有节点信息都是响应式的,无需担心数据的同步问题(这也是二次开发的基础) > - 如果要刷新画布,可直接调用 reload() 方法 > - 此插件主要用于开发一些复杂的图形交互界面,几个常用场景的使用可见 demo 👇 ### 几种使用场景 #### 画布 可处理一些简单的拖拽需求,复杂交互不建议使用。前端在更多场景下可用于图片的拼接、整合、标记操作
![image](./package/public/画布.gif) #### 动画 简单的动画示例
![image](./package/public/动画.gif) #### 截图 以 base64 格式输出截图
![image](./package/public/截图.gif) ## 性能测试 大致性能可见下表( Mac Pro, M1, 2020 ) > 说明: > > 1. 图案个数指画布中实际存在的图案/连线数量; > 1. 响应时长为鼠标 "按下" 至 "抬起" 过程中所花费的响应时间总和; > 1. 加载图案上限约为 1.34w 个; > 1. 本次测试不包含图片,图片加载/响应时长受图片大小/格式影响,具体性能以实际使用情况为准; > 1. 本次测试在设备通电情况下进行,实际使用过程中建议将图案个数控制在 6000 以内,否则会对性能产生较为明显的影响; | 图案个数 | 加载时长 ms | 响应时长 ms | | -------- | ----------- | ----------- | | 60 | 2 | 12 ~ 33 | | 600 | 23 | 72 ~ 103 | | 6000 | 650 ~ 662 | 545 ~ 603 | | 12000 | 2248 ~ 2306 | 1000 ~ 1200 | ## 仓库 代码仓库:[点此跳转](https://gitee.com/xsw1999/montage/tree/master/)
npm:[点此跳转](https://www.npmjs.com/package/montage.js) ## demo 开发示例,仅供参考 ``` cd example npm i npm run serve ``` ## 快速开始 ### 安装 ``` npm install montage.js ``` ### 创建第一组图案 - 1. 创建画布 ```html ``` - 2. 渲染节点 ```js import Montage from "montage.js"; // 初始化 this.draw = new Montage( { drag: true, // 注意:如果要当成画布使用(允许缩放/拖拽),则需要开启 drag ,若只需要生成静态图案,则无需开启此选项 width: 300, height: 300, }, document.getElementById("board") ); // 添加两个节点 this.draw.addNode({ type: "rect", x: 20, y: 50, width: 80, height: 40, zoom: true, // 开启缩放 connect: true, // 开启连线功能 textInfo: { text: "hello world", }, }); this.draw.addNode({ type: "circular", x: 200, y: 70, radiusX: 50, radiusY: 30, zoom: true, connect: true, textInfo: { text: "hello world", }, }); ``` 创建节点后会返回被创建的节点信息 > 注意:只有在调用 addNode 方法时会返回 ```js // 获取创建的节点信息 this.draw .addNode({ type: "rect", x: 100, y: 100, width: 50, height: 50, }) .then((node) => { console.log("node: ", node); }); ``` ### 效果 ![image](./package/public/demo.png) ### 数据导入及导出 当要将画布中的元素保存下来时,可调用 getNodes 方法,该方法会返回画布中的节点信息和连接线信息,导入时需要将这两组数据分别导入 > 同理,如果需要将两个图形连接起来,也可以定义一组 connects 进行导入(可参考 demo 中的示例) ```js // 获取节点信息 let { nodes, connects } = this.draw.getNodes(); // 导入 for (let i of nodes) { this.draw.addNode(i); } for (let i of connects) { this.draw.addConnect(i); } ``` ## 事件监听及回调 ### 监听 当需要使用 addEventListener 监听某些事件时,请不要直接监听 canvas 元素,推荐监听父级元素,示例如下: > 需注意:在鼠标按下并选中了某一个元素后,为了防止不必要的重绘,会渲染一层蒙版,所以鼠标抬起时将无法监听 up / move ... 等事件 ```html
``` ```js document .getElementById("canvasEvent") .addEventListener("mousemove", (event) => { console.log("move", event); }); ``` ### 回调 创建画布时,传入第三个参数作为点击画布的回调,当选中图案时,会将选中的图案作为参数传入 ```js import montage from "montage.js"; function checked(node) => { console.log('checked node', node) } new Montage({ drag: true, width: 300, height: 300, }, document.getElementById('board'), checked); ``` ## 节点配置项及方法 ### 画布配置 > 画布默认为静态画布,只用于展示添加的图案,同时内置了图案的拖拽方法,若需要对画布进行操作,需要将 drag 属性设置为 true > > > 注意:此插件不提供画布整体拖拽 / 缩放功能,整体缩放 / 拖拽的实现可运行 example 后查看示例 | 属性 | 说明 | 可选值 | 默认值 | 类型 | | ------------ | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | width | 画布宽度 | - | - | number | | height | 画布高度 | - | - | number | | drag | 是否开启拖拽操作,如果关闭,则无法更新画布内的图案信息 | true/false | false | boolean | | highlight | 鼠标选中高亮设置
(高亮效果取决于节点的 fillType 属性) | fillColor:高亮颜色; | {
fillColor: 'rgb(0,217,255)',
} | object | | defaultStyle | 默认的元素样式
(可配置默认颜色/默认线宽,当使用自带的绘制方法去绘制图形时会非常有用) | fillColor:默认颜色;
lineWidth:默认宽度(图案 fillType='stroke'时生效);
zoomColor: 缩放框颜色;
zoomWidth: 缩放框线宽;
rotateColor: 旋转框颜色;
rotateWidth: 旋转框宽度;
connectColor: 连接点颜色 | {
fillColor: 'rgba(0, 0, 0, 1)',
lineWidth: 1,
zoomColor: 'rgb(30, 0, 255)',
zoomWidth: 1,
rotateColor: 'rgb(30, 0, 255)',
rotateWidth: 1,
connectColor: 'rgb(19, 154, 251)'
} | object | ### 画布方法 ```js // 初始化 import Montage from "montage.js"; this.ctx = new Montage( { drag: true, width: 300, height: 300, }, document.getElementById("board") ); ``` #### addNode: 往画布中添加节点 用于往画布中添加一个新的节点,可选值见节点配置项 type 属性 ```ts addNode(prop: object) // 传入要创建的节点属性 ``` > 注意:这个操作是异步的,返回创建的节点信息 ```js // prop 见节点配置项 // * 如果指定了节点id,那么在下一次加入相同id节点的时候,将覆盖之前创建的节点 this.ctx.addNode(prop).then((node) => { console.log("new node", node); }); ``` #### getNode: 获取节点信息 获取当前选中的节点信息,或指定 id 的节点信息 > 注意:这将返回一个响应式对象,修改其属性时会自动修改画布内此节点的属性 ```ts // 传入节点 id,返回这个节点的实例 let node = this.ctx.getNode(id: string | null); ``` #### getNodes: 获取画布中的所有图案 获取画布当前状态下的所有图案 ```js let { nodes, // 当前画布中的所有节点 connects, // 当前画布中所有的连接线的信息集合 } = this.ctx.getNodes(); ``` #### deleteNode: 删除某一节点 用于删除某一当前选中的节点,或是根据 id 删除节点 ```ts deleteNode(id: string | null) // 传入节点id ``` ```js let nodeId = "id"; this.ctx.deleteNode(nodeId); ``` #### clearable: 清空画布中所有元素 清空整个画布 ```js this.ctx.clearable(); ``` #### reload: 重绘画布 用于重绘当前画布上的所有节点(刷新) ```js this.ctx.reload(); ``` #### customDrawing: 创建自定义图案 根据图案路径去绘制自定义图案,区别于 addNode 方法,这个方法用于创建一个纯静态展示的复杂图案 > 注意:纯静态图案,仅用于展示,更新时需要使用 reload 方法更新画布 ```ts // 传入要绘制的图案路径信息,详见 自定义图案配置项 this.ctx.customDrawing(node: object) ``` ```js // 这是一个绘制星星的示例 let custom = { start: { x: 200, y: 200, }, lineWay: [ [230, 100], [260, 200], [360, 230], [260, 260], [230, 360], [200, 260], [100, 230], [200, 200], ], fillType: "stroke", fillColor: "rgba(200,200,100,0.8)", }; this.ctx.customDrawing(custom); ``` #### addConnect: 配置两个不同节点间的连线 将两个图案使用连线进行连接,目前只支持曲线和箭头曲线两种连线 ```ts addConnect(connect: object) // 传入节点的连线信息 ``` ```js let connect = {}; // 详见连接线配置项 this.ctx.addConnect(connect); ``` #### setNodeType: 设置图案类型 ```ts // 传入图案类型, 自定义图案属性,及辅助属性, 无返回值 setNodeType(type: string, customProp: object | null, auxProps: object | null) ``` > 注意:只有在手动绘制图案时有效 用于设置即将在画布中绘制的图案类型 ```js // type可选值为 rect: 矩形 / line: 线 / circular: 圆形 let type = 'rect'; // 自定义的节点属性,这些属性将被原封不动的保存在节点中 let customProp = { prop1: 'xx', prop2: 'xx', ... }; // CanvasRenderingContext2D 的一些属性,实际使用频率较低,逐步开放中 // 目前仅支持 globalCompositeOperation let auxProps = { globalCompositeOperation: '', // 设置合成的操作类型,详见 https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation } this.ctx.setNodeType(type, customProp, auxProps) ``` ### 节点配置项 > 对于使用 getNode 方法获取的节点对象,插件内部使用 Proxy 来响应外界对节点的操作,为了防止频繁更改节点属性带来不必要的刷新,在节点更改完成后需要调用 reload 方法更新画布的展示效果 > 关于层级的说明: > > - 图层按照层级从小到大排序,当点击位置包含多个图案,则会优先选中层级较大的图案 > - 由于图片是异步加载,为了防止遮挡其他图案,加载时会置底,但是选取时仍会受到层级影响 > - 当前选中的图案会默认置顶,取消选中后恢复原层级 | 属性 | 说明 | 可选值 | 默认值 | 类型 | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | | id | 节点 id,每次添加节点时会自动验证是否重复
当未填写 id 时,会自动填充,如果需要固定 id,请指定 | - | - | - | | type | 节点类型 | rect: 矩形
circular: 圆形
line: 线
image: 图片 | - | string | | x, y | 起点 x 坐标, 起点 y 坐标 | - | - | number | | width, height | 图案宽高 | - | - | number | | src | 自定义图片路径(type='image'时生效) | - | - | string | | radiusX, radiusY | 自定义圆的半径(type='circular'时生效) | - | - | number | | endX, endY | 终点坐标(type='line'时生效) | - | - | number | | rotate | 旋转弧度,n\*Math.PI | - | 0 | number | | level | 层级 | - | 1 | number | | zoom | 允许缩放/旋转(画布允许拖拽时生效) | true/false | false | boolean | | connect | 允许连线(画布允许拖拽时生效) | true/false | false | boolean | | drag | 允许拖拽(画布允许拖拽时生效) | true/false | true | boolean | | unClick | 禁止选中
(当无法被选中时,将无法响应式的更新该图案配置,修改配置后需要先调用 deleteNode 方法删除,再调用 addNode 方法重新加载) | true/false | false | boolean | | unLight | 选中后禁止高亮 | true/false | false | boolean | | fillColor | 填充颜色 | color/rgb()/rgba() | rgb(0,0,0) | string | | fillType | 类别 | fill/stroke | stroke | string | | lineWidth | 宽度(fillType = 'stroke'时生效) | - | 1 | number | | textInfo | 文字信息
\* 注意:文字换行可使用 /n 来处理,文字缩进可直接使用空格填充 | text: string,
font: string(与 css font 属性用法相同),
fillType: 'fill/stroke',
fillColor: 'color/rgb()/rgba()' | - | object | ### 连接线配置项 连线用于配置不同图案之间的连接关系,目前仅支持曲线、剪头曲线; > 需要注意: > > - 在实际连线过程中,将每一个图形分成了上右下左四个方位,分别对应 1、2、3、4 号位置,节点中的 pos 属性为此位置的序号 > - 需要确保开始节点和目标节点的已经存在并且 id 完全一致,否则会失效 | 属性 | 说明 | 可选值 | | ---------- | -------------------------------------------------- | ------------------------------------------------- | | id | 连接线 id | ----- | | lineWidth | 连接线宽度(注意:如果使用了箭头,不建议设置过大) | number | | fillColor | 连接线颜色 | string | | startInfo | 开始节点的信息 | {
id: 开始节点的 id
pos: 开始的位置
} | | targetInfo | 目标节点的信息 | {
id: 目标节点的 id
pos: 结束的位置
} | ### 自定义图案配置项 用于创建一个纯静态的自定义图案,由于是根据路径绘制的,所以这个图案可以是一个复杂图形 | 属性 | 说明 | 可选值 | | --- | --- | ----- | | start | 开始位置 | { x, y } | | lineWay | 绘制的路径,将按照路径顺序进行绘制 | [[x, y], [x1, y1], ...] | | fillType | 图案类型 | fill: 实心 / stroke: 空心 | | fillColor | 图案颜色 | rgba() | | lineWidth | 线宽,在 filleType = 'stroke'时生效 | number |