# 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 👇
### 几种使用场景
#### 画布
可处理一些简单的拖拽需求,复杂交互不建议使用。前端在更多场景下可用于图片的拼接、整合、标记操作

#### 动画
简单的动画示例

#### 截图
以 base64 格式输出截图

## 性能测试
大致性能可见下表( 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);
});
```
### 效果

### 数据导入及导出
当要将画布中的元素保存下来时,可调用 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 |