最近查找js相关的鱼骨图组件,找了半天都没有合适的,自己参考gojs官网demo简单的实现了下,效果如下。

废话少说,直接上代码。

引入gojs

npm install gojs --save

整合到vue

fishbone.vue

<template><div><div id="gos" class="lean"></div><el-button @click="layoutFishbone">layoutFishbone</el-button><el-button @click="layoutBranching">layoutBranching</el-button><el-button @click="layoutNormal">layoutNormal</el-button></div>
</template><script>
import go from 'gojs'
import { FishboneLayout, FishboneLink } from '../assets/FishboneLayout.js';
export default {data () {return {diagram: '',json: {'text': 'Incorrect Deliveries', 'size': 18, 'weight': 'Bold', 'causes': [{'text': 'Skills', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'knowledge', 'weight': 'Bold', 'causes': [{'text': 'procedures', 'causes': [{ 'text': 'documentation' }]},{ 'text': 'products' }]},{ 'text': 'literacy', 'weight': 'Bold' }]},{'text': 'Procedures', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'manual', 'weight': 'Bold', 'causes': [{ 'text': 'consistency' }]},{'text': 'automated', 'weight': 'Bold', 'causes': [{ 'text': 'correctness' },{ 'text': 'reliability' }]}]},{'text': 'Communication', 'size': 14, 'weight': 'Bold', 'causes': [{ 'text': 'ambiguity', 'weight': 'Bold' },{'text': 'sales staff', 'weight': 'Bold', 'causes': [{'text': 'order details', 'causes': [{ 'text': 'lack of knowledge' }]}]},{'text': 'telephone orders', 'weight': 'Bold', 'causes': [{ 'text': 'lack of information' }]},{'text': 'picking slips', 'weight': 'Bold', 'causes': [{ 'text': 'details' },{ 'text': 'legibility' }]}]},{'text': 'Transport', 'size': 14, 'weight': 'Bold', 'causes': [{'text': 'information', 'weight': 'Bold', 'causes': [{ 'text': 'incorrect person' },{'text': 'incorrect addresses', 'causes': [{'text': 'customer data base', 'causes': [{ 'text': 'not up-to-date' },{ 'text': 'incorrect program' }]}]},{ 'text': 'incorrect dept' }]},{'text': 'carriers', 'weight': 'Bold', 'causes': [{ 'text': 'efficiency' },{ 'text': 'methods' }]}]}]}}},mounted () {const $ = go.GraphObject.make;let _this = this;this.diagram = $(go.Diagram, 'gos', { isReadOnly: false })this.diagram.nodeTemplate =$(go.Node,$(go.TextBlock,new go.Binding('text'),new go.Binding('font', '', _this.convertFont)));this.diagram.linkTemplateMap.add('normal',$(go.Link,{ routing: go.Link.Orthogonal, corner: 4 },$(go.Shape)));this.diagram.linkTemplateMap.add('fishbone',$(FishboneLink, // defined above$(go.Shape)));const nodeDataArray = [];_this.walkJson(_this.json, nodeDataArray);this.diagram.model = new go.TreeModel(nodeDataArray);this.layoutFishbone();},methods: {convertFont (data) {let size = data.size;if (size === undefined)size = 13;let weight = data.weight;if (weight === undefined)weight = '';return weight + ' ' + size + 'px sans-serif';},walkJson (obj, arr) {const key = arr.length;obj.key = key;arr.push(obj);const children = obj.causes;if (children) {for (let i = 0; i < children.length; i++) {const o = children[i];o.parent = key;this.walkJson(o, arr);}}},layoutFishbone () {this.diagram.startTransaction('fishbone layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('fishbone');this.diagram.layout = go.GraphObject.make(FishboneLayout, {angle: 180,layerSpacing: 10,nodeSpacing: 20,rowSpacing: 10});this.diagram.commitTransaction('fishbone layout');},layoutBranching () {this.diagram.startTransaction('branching layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('normal');this.diagram.layout = go.GraphObject.make(go.TreeLayout, {angle: 180,layerSpacing: 20,alignment: go.TreeLayout.AlignmentBusBranching});this.diagram.commitTransaction('branching layout');},layoutNormal () {this.diagram.startTransaction('normal layout');this.diagram.linkTemplate = this.diagram.linkTemplateMap.getValue('normal');this.diagram.layout = go.GraphObject.make(go.TreeLayout, {angle: 180,breadthLimit: 1000,alignment: go.TreeLayout.AlignmentStart});this.diagram.commitTransaction('normal layout');}}
}
</script><style scoped>
.lean {height: 500px;width: 90%;border: 1px solid black;background-color: #dae4e4;
}
</style>

FishboneLayout.js


import go from "gojs";/*** FishboneLayout is a custom {@link Layout} derived from {@link TreeLayout} for creating "fishbone" diagrams.* A fishbone diagram also requires a {@link Link} class that implements custom routing, {@link FishboneLink}.** This only works for angle === 0 or angle === 180.** This layout assumes Links are automatically routed in the way needed by fishbone diagrams,* by using the FishboneLink class instead of go.Link.** If you want to experiment with this extension, try the <a href="../../extensionsTS/Fishbone.html">Fishbone Layout</a> sample.* @category Layout Extension*/
export class FishboneLayout extends go.TreeLayout {/*** Constructs a FishboneLayout and sets the following properties:*   - {@link #alignment} = {@link TreeLayout.AlignmentBusBranching}*   - {@link #setsPortSpot} = false*   - {@link #setsChildPortSpot} = false*/constructor() {super();this.alignment = go.TreeLayout.AlignmentBusBranching;this.setsPortSpot = false;this.setsChildPortSpot = false;}/*** Create and initialize a {@link LayoutNetwork} with the given nodes and links.* This override creates dummy vertexes, when necessary, to allow for proper positioning within the fishbone.* @param {Diagram|Group|Iterable.<Part>} coll A {@link Diagram} or a {@link Group} or a collection of {@link Part}s.* @return {LayoutNetwork}*/makeNetwork(coll) {// assert(this.angle === 0 || this.angle === 180);// assert(this.alignment === go.TreeLayout.AlignmentBusBranching);// assert(this.path !== go.TreeLayout.PathSource);// call base method for standard behaviorconst net = super.makeNetwork(coll);// make a copy of the collection of TreeVertexes// because we will be modifying the TreeNetwork.vertexes collection in the loopconst verts = new go.List().addAll(net.vertexes.iterator);verts.each(function(v) {// ignore leaves of treeif (v.destinationEdges.count === 0) return;if (v.destinationEdges.count % 2 === 1) {// if there's an odd number of real children, add two dummiesconst dummy = net.createVertex();dummy.bounds = new go.Rect();dummy.focus = new go.Point();net.addVertex(dummy);net.linkVertexes(v, dummy, null);}// make sure there's an odd number of children, including at least one dummy;// commitNodes will move the parent node to where this dummy child node is placedconst dummy2 = net.createVertex();dummy2.bounds = v.bounds;dummy2.focus = v.focus;net.addVertex(dummy2);net.linkVertexes(v, dummy2, null);});return net;}/*** Add a direction property to each vertex and modify {@link TreeVertex#layerSpacing}.*/assignTreeVertexValues(v) {super.assignTreeVertexValues(v);v["_direction"] = 0; // add this property to each TreeVertexif (v.parent !== null) {// The parent node will be moved to where the last dummy will be;// reduce the space to account for the future hole.if (v.angle === 0 || v.angle === 180) {v.layerSpacing -= v.bounds.width;} else {v.layerSpacing -= v.bounds.height;}}}/*** Assigns {@link Link#fromSpot}s and {@link Link#toSpot}s based on branching and angle* and moves vertexes based on dummy locations.*/commitNodes() {if (this.network === null) return;// vertex Angle is set by BusBranching "inheritance";// assign spots assuming overall Angle === 0 or 180// and links are always connecting horizontal with verticalthis.network.edges.each(function(e) {const link = e.link;if (link === null) return;link.fromSpot = go.Spot.None;link.toSpot = go.Spot.None;const v = e.fromVertex;const w = e.toVertex;if (v.angle === 0) {link.fromSpot = go.Spot.Left;} else if (v.angle === 180) {link.fromSpot = go.Spot.Right;}if (w.angle === 0) {link.toSpot = go.Spot.Left;} else if (w.angle === 180) {link.toSpot = go.Spot.Right;}});// move the parent node to the location of the last dummylet vit = this.network.vertexes.iterator;while (vit.next()) {const v = vit.value;const len = v.children.length;if (len === 0) continue; // ignore leaf nodesif (v.parent === null) continue; // don't move root nodeconst dummy2 = v.children[len - 1];v.centerX = dummy2.centerX;v.centerY = dummy2.centerY;}const layout = this;vit = this.network.vertexes.iterator;while (vit.next()) {const v = vit.value;if (v.parent === null) {layout.shift(v);}}// now actually change the Node.location of all nodessuper.commitNodes();}/*** This override stops links from being committed since the work is done by the {@link FishboneLink} class.*/commitLinks() {}/*** Shifts subtrees within the fishbone based on angle and node spacing.*/shift(v) {const p = v.parent;if (p !== null && (v.angle === 90 || v.angle === 270)) {const g = p.parent;if (g !== null) {const shift = v.nodeSpacing;if (g["_direction"] > 0) {if (g.angle === 90) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 270) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 90) this.shiftAll(-2, shift, p, v);}} else if (g.angle === 270) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 90) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 270) this.shiftAll(-2, shift, p, v);}}} else if (g["_direction"] < 0) {if (g.angle === 90) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 90) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 270) this.shiftAll(-2, shift, p, v);}} else if (g.angle === 270) {if (p.angle === 0) {v["_direction"] = 1;if (v.angle === 270) this.shiftAll(2, -shift, p, v);} else if (p.angle === 180) {v["_direction"] = -1;if (v.angle === 90) this.shiftAll(-2, shift, p, v);}}}} else {// g === null: V is a child of the tree ROOTconst dir = p.angle === 0 ? 1 : -1;v["_direction"] = dir;this.shiftAll(dir, 0, p, v);}}for (let i = 0; i < v.children.length; i++) {const c = v.children[i];this.shift(c);}}/*** Shifts a subtree.*/shiftAll(direction, absolute, root, v) {// assert(root.angle === 0 || root.angle === 180);let locx = v.centerX;locx += (direction * Math.abs(root.centerY - v.centerY)) / 2;locx += absolute;v.centerX = locx;for (let i = 0; i < v.children.length; i++) {const c = v.children[i];this.shiftAll(direction, absolute, root, c);}}
}
/*** Custom {@link Link} class for {@link FishboneLayout}.* @category Part Extension*/
export class FishboneLink extends go.Link {computeAdjusting() {return this.adjusting;}/*** Determines the points for this link based on spots and maintains horizontal lines.*/computePoints() {const result = super.computePoints();if (result) {// insert middle point to maintain horizontal linesif (this.fromSpot.equals(go.Spot.Right) ||this.fromSpot.equals(go.Spot.Left)) {let p1;// deal with root node being on the "wrong" sideconst fromnode = this.fromNode;const fromport = this.fromPort;if (fromnode !== null &&fromport !== null &&fromnode.findLinksInto().count === 0) {// pretend the link is coming from the opposite direction than the declared FromSpotconst fromctr = fromport.getDocumentPoint(go.Spot.Center);const fromfar = fromctr.copy();fromfar.x += this.fromSpot.equals(go.Spot.Left) ? 99999 : -99999;p1 = this.getLinkPointFromPoint(fromnode,fromport,fromctr,fromfar,true).copy();// update the route pointsthis.setPoint(0, p1);let endseg = this.fromEndSegmentLength;if (isNaN(endseg)) endseg = fromport.fromEndSegmentLength;p1.x += this.fromSpot.equals(go.Spot.Left) ? endseg : -endseg;this.setPoint(1, p1);} else {p1 = this.getPoint(1); // points 0 & 1 should be OK already}const tonode = this.toNode;const toport = this.toPort;if (tonode !== null && toport !== null) {const toctr = toport.getDocumentPoint(go.Spot.Center);const far = toctr.copy();far.x += this.fromSpot.equals(go.Spot.Left) ? -99999 / 2 : 99999 / 2;far.y += toctr.y < p1.y ? 99999 : -99999;const p2 = this.getLinkPointFromPoint(tonode,toport,toctr,far,false);this.setPoint(2, p2);let dx = Math.abs(p2.y - p1.y) / 2;if (this.fromSpot.equals(go.Spot.Left)) dx = -dx;this.insertPoint(2, new go.Point(p2.x + dx, p1.y));}} else if (this.toSpot.equals(go.Spot.Right) ||this.toSpot.equals(go.Spot.Left)) {const p1 = this.getPoint(1); // points 1 & 2 should be OK alreadyconst fromnode = this.fromNode;const fromport = this.fromPort;if (fromnode !== null && fromport !== null) {const parentlink = fromnode.findLinksInto().first();const fromctr = fromport.getDocumentPoint(go.Spot.Center);const far = fromctr.copy();far.x +=parentlink !== null && parentlink.fromSpot.equals(go.Spot.Left)? -99999 / 2: 99999 / 2;far.y += fromctr.y < p1.y ? 99999 : -99999;const p0 = this.getLinkPointFromPoint(fromnode,fromport,fromctr,far,true);this.setPoint(0, p0);let dx = Math.abs(p1.y - p0.y) / 2;if (parentlink !== null && parentlink.fromSpot.equals(go.Spot.Left))dx = -dx;this.insertPoint(1, new go.Point(p0.x + dx, p1.y));}}}return result;}
}

vue+gojs 绘制鱼骨图(因果图)相关推荐

  1. Vue + gojs 绘制鱼骨图

    效果图: 引入gojs npm install gojs --save 在git地址拉下代码 ,地图组件地址是 src\components\fishboneDiagram git地址 开源不易 多多 ...

  2. 利用思维导图软件绘制鱼骨图怎样做

    思维导图的类型有哪些? 圆圈图 主要用于把一个主题展开来,联想或描述细节.小圈圈是主题,而外面的大圈圈里放的是和这个主题有关的细节或特征 气泡图 由很多泡泡组成,中间一个主题泡泡描述核心主题,周围的属 ...

  3. vue、jtopo绘制鱼骨图

    效果如下图所示: 一.文件目录结构: 二.绘制png的鱼头.鱼尾图片 fish_head.png:      fish_tail.png:      三.下载js文件 jquery.jtopo请到官网 ...

  4. 绘制鱼骨图,卡壳了,找到了别人的文章,先好好学习理论知识。

    鱼骨图的三种类型 A.整理问题型鱼骨图(各要素与特性值间不存在原因关系,而是结构构成关系) B.原因型鱼骨图(鱼头在右,特性值通常以"为什么--"来写) C.对策型鱼骨图(鱼头在左 ...

  5. XMind是怎么绘制鱼骨图

    鱼骨图因常常被用于问题的分析以及解决方法,所以又被称为因果图.鱼骨图一直是XMind思维导图软件的拿手好戏之一,一直被模仿从未被超越.让我们一起通过本篇文章,探讨XMind鱼骨图的秘密吧. 当你打开X ...

  6. 如何用XMind绘制鱼骨图

    鱼骨图因常常被用于问题的分析以及解决方法,所以又被称为因果图.鱼骨图一直是XMind思维导图软件的拿手好戏之一,一直被模仿从未被超越.让我们一起通过本篇文章,探讨XMind鱼骨图的秘密吧. 当你打开X ...

  7. VUE+Cesium绘制迁徙图结合echarts实现

    一.编写EchartsLayer类实现注入到window对象,实现类如下: import * as Cesium from 'cesium/Cesium' import * as echarts fr ...

  8. vue + canvas绘制背景图、矩形

    最近有需求做图片人脸识别,在系统上传图片后,后台返回坐标将识别到的人脸画上矩形. 效果图如下: 实现过程: 1.利用绘制canvas实例 //html <canvas id='imgCanvas ...

  9. linux画鱼骨图插件,鱼骨图绘制方法:超实用的鱼骨图制作软件

    鱼骨图又叫石川图或因果图,它可以帮助你分析一个问题的所有可能原因.说到绘制鱼骨图的工具,有人用办公常用的Word,也有人用Visio.本文将为您推荐一款无需任何绘图功底,也可以轻松上手的鱼骨图绘制软件 ...

最新文章

  1. uwsgi模式_nginx+uwsgi 和nginx+gunicorn区别、如何部署
  2. Nauuo and Chess
  3. Linux下Bluez的编程实现
  4. Google强化学习框架SEED RL环境部署
  5. java单例模式和HashMap的线程安全
  6. maven:同一个项目内模块之间互相调用
  7. pytorch一天速成第一部分——基础入门Tensor和cuda
  8. css渐变颜色php,css的渐变颜色
  9. 社区口碑营销案例分析
  10. 走自己的路,让国际米兰连胜去吧!(写给米兰球迷)
  11. 《明解C语言 入门篇》第4章 程序的循环控制 练习题解答
  12. linux ps auxf,ps -aux命令详解
  13. TTL(UART)信号和RS232信号 对比
  14. 学习如何读论文的一些磨刀不误砍柴工
  15. 【记录读论文时遇到的一些算法2】—— Occupancy Grid Map
  16. uber优步提高成单率,轻松拿奖励!
  17. 四种解决Vue中重复点击相同路由控制台报错问题( Avoided redundant navigation to current location)
  18. Javascript判断日期是否合法
  19. 微信小程序使用vant ui 组件库安装步骤
  20. python控制电脑开机后进不了系统_电脑开机进不了系统原因以及解决方法

热门文章

  1. cadence绘制schematic时连线方式改变
  2. Ubuntu子系统VcXsrv黑屏compiz (core)
  3. 对一个补丁工具程序的技术细节分析(WIN32汇编)
  4. Android活动生命周期
  5. wav文件提取出pcm数据
  6. Quantifier (logic)
  7. java学习笔记-HTML01
  8. SpringBoot:LoggingException: log4j-slf4j-impl
  9. 在ubuntu18.04中怎么测试麦克风是否可用
  10. 阿里巴巴DBA成长之路