jsMind使用踩坑记录与个人总结(vue2+elementui)

首先是安装包,目前版本是0.8.7

npm install jsmind

注意,如果你也是vue2,那直接引入的时候可能会失败。

import 'jsmind/style/jsmind.css' //样式
import jsMind from 'jsmind' //这个是正常引入
//import jsMind from 'jsmind/js-legacy/jsmind.js'  //引入失败的话可以试试这个,不过该方式下引入将无法使用新版选项更改样式或渲染方式之类的

如果要正常引入,则需要在vue.config.js中添加如下代码

module.exports = {
  transpileDependencies: ['jsmind'],
  ……

如果是vue3,则不会有问题。

 

接下来是使用,具体选项配置可查看官网文档get-started | zh | docs | jsMind

 

主要是几个坑:

1、绘图必须要存在实体才能进行,因此若是要用弹窗等形式展示的话,最好确保包裹的父元素出现了再进行初始化

1     handleClick(){
2       this.dialogVisbile = true //弹窗显示
3       this.jsMindLoading = true //加载
4       this.$nextTick(()=>{
5         this.jsChart = new jsMind(this.jsoptions) //初始化jsmind实例
6         this.jsChart.show(this.mind) //加载数据,渲染节点
7       })
8     },

2、jsmind不提供自带的销毁api,因此如果是在弹窗之类的进行渲染的话,再次点开会导致上一次的图还保留着,同时再次重复渲染一次(即使元素id理论上来说不能重复),所以需要手动销毁

1     handleClose() {
2       if (this.jsChart) delete this.jsChart //删除实例对象
3       const container = document.getElementById('jsmind_container')
4       while (container.firstChild) container.removeChild(container.firstChild) //获取父容器,并循环删除其下节点,如果加了el-loading的遮罩层,也可一并删除
5       this.mind.data.children = [] //这里是子节点的数据,可以在请求接口时清空,或者此时清空
6       this.dialogVisible = false
7     },

3、可以支持自定义绘制连线与节点(此处是在vue文件的script内)连线可用canvas或svg绘制,canvas方式返回的是canvas上下文,可直接调用方法进行绘制;而svg返回的是"path"这个dom对象,绘制时需要添加属性:

 1 export default {
 2   data() {
 3     const printArrow = ({ ctx, start_point, end_point }, str) =>{
 4             //获取根节点高度与宽度,因为实际终点是在根节点的中心位置,因此需要重新计算其连线的边界y值与x值。这里的nodeid就是根节点的id,也就是数据集mind.data.id
 5             let height = document.querySelector(`[nodeid=${str}]`).offsetHeight
 6             let width = document.querySelector(`[nodeid=${str}]`).offsetWidth
 7             console.log('height', height)
 8             //起点始终是子节点,终点始终是父节点,因此基础路径一致,只是箭头方向需要变化
 9             //左边的选项
10             if(start_point.x < end_point.x){
11             //左边——上边的选项
12               if(start_point.y < end_point.y){
13                 ctx.setAttribute('d', `M${start_point.x},${start_point.y}
14                 L${(start_point.x + end_point.x) / 2},${start_point.y}
15                 L${(start_point.x + end_point.x) / 2},${end_point.y - height / 2 - 4 }
16                 L${(start_point.x + end_point.x) / 2 - 3},${end_point.y - height / 2 - 4 }
17                 L${(start_point.x + end_point.x) / 2},${end_point.y - height / 2 }
18                 L${(start_point.x + end_point.x) / 2 + 3},${end_point.y - height / 2 - 4 }
19                 L${(start_point.x + end_point.x) / 2},${end_point.y - height / 2 - 4 }
20                 L${(start_point.x + end_point.x) / 2},${start_point.y}
21                 L${start_point.x},${start_point.y}`)
22               } //左边——中间的选项
23               else if(start_point.y === end_point.y) {
24                 ctx.setAttribute('d', `M${start_point.x},${start_point.y}
25                 L${end_point.x - width / 2 - 4},${start_point.y}
26                 L${end_point.x - width / 2 - 4},${start_point.y + 3}
27                 L${end_point.x - width / 2},${start_point.y}
28                 L${end_point.x - width / 2 - 4},${start_point.y - 3}
29                 L${end_point.x - width / 2 - 4},${start_point.y}
30                 L${start_point.x},${start_point.y}`)
31               } // 左边——下边的选项
32               else if(start_point.y > end_point.y){
33                 ctx.setAttribute('d', `M${start_point.x},${start_point.y}
34                 L${(start_point.x + end_point.x) / 2},${start_point.y}
35                 L${(start_point.x + end_point.x) / 2},${end_point.y + height / 2 + 8}
36                 L${(start_point.x + end_point.x) / 2 - 3},${end_point.y + height / 2 + 8}
37                 L${(start_point.x + end_point.x) / 2},${end_point.y + height / 2 + 4}
38                 L${(start_point.x + end_point.x) / 2 + 3},${end_point.y + height / 2 + 8}
39                 L${(start_point.x + end_point.x) / 2},${end_point.y + height / 2 + 8}
40                 L${(start_point.x + end_point.x) / 2},${start_point.y}
41                 L${start_point.x},${start_point.y}`)
42               }
43             } //右边的节点,统一一个方向箭头
44             else if(start_point.x > end_point.x){
45               ctx.setAttribute('d', `M${(start_point.x + end_point.x) / 2},${end_point.y}
46                 L${(start_point.x + end_point.x) / 2},${start_point.y}
47                 L${start_point.x - 4},${start_point.y}
48                 L${start_point.x - 4},${start_point.y + 3}
49                 L${start_point.x},${start_point.y}
50                 L${start_point.x - 4},${start_point.y - 3}
51                 L${start_point.x - 4},${start_point.y}
52                 L${(start_point.x + end_point.x) / 2},${start_point.y}
53                 L${(start_point.x + end_point.x) / 2},${end_point.y}`)
54             }
55             ctx.setAttribute('stroke-width', 2)
56             ctx.setAttribute('stroke', 'rgb(85,89,198)')
57             ctx.setAttribute('fill', 'rgb(85,89,198)')
58     }
59     const printRootBorder = (jm, element, node) => {
60             //jm为jsMind实例,不用
61             //element为节点元素
62             //node为节点数据
63             //返回true则意味着不再渲染该节点,取而代之使用自定义渲染;false则为默认渲染方式
64             if(['root_hardware', 'root_software'].includes(node.id)){
65               element.innerHTML = node.topic 
66               element.style.border = '2px solid red'
67               return true
68             } else return false; 
69     }
70     return {
71       jsoptions: {
72         container: 'jsmind_container',
73         theme: 'primary',
74         editable: true,
75         mode: 'full',
76         view: {
77           engine: 'svg',
78           custom_line_render: ({ ctx, start_point, end_point }) => printArrow({ ctx, start_point, end_point }, 'root_hardware'), //可以直接在这里用匿名函数绘制,不过如果有多个图需要绘制,而他们又拥有同样的渲染样式,那就可以抽取出来定义后使用
79           custom_node_render: printRootBorder
80         },
81     ……
82       },
83     }
84 }

需要注意的是,开始点永远是在子节点上,而结束点是出于根节点中心位置,因此若是要绘制左右两侧的箭头,那么从左边来的连线就要获取根节点的内置高度与宽度,从而计算出连线与其交汇的位置(上面的方法实现)

如果不更改边框的话,正常来说左侧箭头的上下部分绘制时是加减相同的偏差值(即根节点一半高度+箭头本身高度),但不知为何加入了边框后,下方的箭头必须要加上两倍边框的偏差值才行,否则会被边框挡住……

同样的,因为path绘制默认会连接起点到终点部分,

 如果为了美观,可以考虑怎么做成曲线连接

 

posted @ 2025-04-02 15:39  RicardoX3  阅读(569)  评论(0)    收藏  举报