使⽤js实现思维导图
本⽂主要阐述使⽤js实现思维导图的关键技术点,如果还不知道什么是思维导图的同学,请⾃⾏度娘。以下是demo和源码的传送门:
demo:
源码:
下载:
在源码中我使⽤了svg绘制思维导图。与canvas相⽐,svg将图像当成对象,我们可将思维导图中节点和线等图形表现为对象,⽽且svg更适合⽤于动态交互的应⽤
下⾯介绍⼏个关键技术点:
⼦节点位置的重绘
⼀个基本的思维导图⼯具应该拥有增加节点和删除节点的功能。在某个节点上增删节点时,为了使得所有⼦节点的⾼度相对于该节点垂直居中,都会重新渲染⼦节点的垂直位置。
如图1所⽰,⾸先求得⽗节点的中⼼点F的坐标为(hfx, hfy),设⽗节点与⼦节点的⽔平距离为interval,⽗节点的宽为parentWidth。作⽔平线段FC,C点的横坐标即为⼦节点的横坐标childX。如下图所⽰:
使⽤js实现思维导图
使⽤js实现思维导图
为 了让⼦节点间垂直隔开,每⼀个⼦节点上下都有补⽩,所以⼀个⼦节点所占的区域⾼度为该⼦节点的节点⾼度加上两个补⽩⾼度。迭代所有⼦节点,求取所有⼦节 点的区域⾼度areaHeight,然后在线段FC的C点上作⼀条长度为areaHeight的垂直平分线AB,所有⼦节点的垂直区域都在垂直平分线AB 内,这样可以保证所有⼦节点的⾼度相对于该节点垂直居中。如下图所⽰:
使⽤js实现思维导图
使⽤js实现思维导图
我 们需要求得每⼀个⼦节点的垂直坐标childY。⾸先求得A点的垂直坐标startY = hfy – areaHeight / 2,第⼀个⼦节点的垂直坐标由startY加padding可得。求第⼆个⼦节点的垂直坐标时,startY累加上⼀个⼦节点的区域⾼度,则第⼆个⼦节点 的垂直坐标等于当前startY 加上padding。之后的⼦节点通过迭代相同的操作可得。在每⼀轮迭代中,根据求得的⼦节点坐标(childX, childY)渲染节点的位置。如下
图所⽰:
使⽤js实现思维导图
使⽤js实现思维导图
实现代码如下:
Js代码
1. / 以下变量请⾃⾏求得
2. var hfx, // ⽗节点的中⼼x轴坐标
3. hfy, // ⽗节点的中⼼y轴坐标
4. parentWidth, // ⽗节点的宽度
5. children, // ⼦节点列表
6. padding, // ⼦节点垂直间距
7. interval; // 节点间⽔平间距
8.
9. var childX, // ⼦节点的x轴坐标
10. startY, // ⼦节点区域的起始坐标
11. childrenAreaHeight = 0; // ⼦节点总区域⾼度
12.
13.
14. childX = hfx + parentWidth / 2 + interval;
15.
16. // 迭代⼦节点,求得⼦节点总区域⾼度
17. children.forEach(function(child){
18. var curAreaHeight = getNodeHeight(child) + padding * 2;
19. childrenAreaHeight += curAreaHeight;
20. });
21.
22. startY = hfy - childrenAreaHeight / 2;
23. // 迭代⼦节点,求得每个⼦节点的垂直坐标
24. children.forEach(function(child){
25. var childY = startY + padding;
26.
27. // 已经求得当前⼦节点坐标(childX, childY),在这⾥作渲染操作
28.
29. var curAreaHeight = getNodeHeight(child) + padding * 2;
30. startY += curAreaHeight;    // 其实⾼度累加
31. });
32.
33.
34. /**
35. * 获取节点的⾼度
36. */
37. function getNodeHeight(){
38. // ...
39. }
祖先节点的同级节点的垂直位置调整
如下图所⽰,当增加⼀个节点时,该节点⽗节点的同级节点需要被“撑开”:设该节点的1/2区域⾼度为moveY,在⽗节点的同级节点中,⽐⽗节点⾼的向上 偏移⼀个moveY,⽐⽗节点低的向下偏移⼀个moveY。⽗节点的⽗节点的同级节点也做相同的处理,⼀直递归到根节点为⽌。当删除⼀个节点时,节点的⽗ 节点的同级节点会被“压低”,“压低”操作和上述操作相似。注意,当增加第⼀个⼦节点和删除最后⼀个⼦节点时,不会进⾏“撑开”和“压低”操作。
使⽤js实现思维导图
使⽤js实现思维导图
实现源码如下:
1. /**
2. * 调整当前的⽗节点的同级节点的位置
3. * @param node 当前的⽗节点, 以下为该节点需要⽤到的属性
4. *              node.father: 节点的⽗节点,为null时表⽰⽗节点为根节点
5. *              node.children:  节点的⼦节点列表
6. *              node.x:  节点的x轴坐标
7. *              node.y: 节点的y轴坐标
8. *
9. * @oaram areaHeight 被操作节点的区域⾼度
10. */
11. function resetBrotherPosition(node, areaHeight){
12.    var brother,                    // 同级节点
13.        moveY = areaHeight / 2;    // 需要移动的⾼度
14.    if(node.father){
15.        node.father.children.forEach(function(curNode){
16.                // 遍历同级节点
17.                if(curNode != node){
18.                    if(brother.y < node.y){
19.                        // 向上移动brother节点的代码写在这
20.                    }
21.                    else {
22.                        // 向下移动brother节点的代码写在这
23.                    }
24.                }
25.            }
26.        );
27.    }
28.    // 递归⽗节点
29.    if(node.father){
30.        resetBrotherPosition(node.father, areaHeight);
31.    }
svg图
32. }
拖动节点
当拖动根节点时,通过改变svg的视⼝坐标来实现拖动整个思维导图的效果。当拖动
⾮根节点时,会按顺序触发mouseup、mousemove、mousedown三个事件,分别对应按下⿏标、⿏
标移动和放下⿏标三个状态。在按下⿏标 状态下,会以当前节点为原型克隆⼀个节点⽤于占位。在拖动⿏标状态下,通过改变节点的坐标实现节点位置的改变。在放下⿏标状态下,会判断当前节点是否与其 他节点重叠,如果重叠则使重叠节点变为当前节点的⽗节点,否则,当前节点返回原来的位置。
其他技术点我就不⼀⼀列出来了,有兴趣的同学可以到上⾯的传送门看看源码。
转⾃:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。