用js+svg實現一個簡易的思惟腦圖

功能

這是一個用js寫的簡易思惟腦圖, 大約用了100多行代碼,效果以下圖

image.png

有的功能

  • 只能把數據展現成思惟腦圖!
  • 可自定義字體顏色、邊框顏色、邊框背景、連線顏色!

沒有的功能

  • 不能編輯!
  • 不能縮放!
  • 不能展開收縮!

差什麼功能小夥伴們能夠自行DIY一下,後期可能會補足這些功能!vue

對mvvm框架的支持

我爲公司改過一版適用angular框架的,受限於angular render2,我在代碼中用了幾個setTimeout(),感受很不優雅,就不提供給小夥伴了。
純潔的js代碼,小夥伴能夠DIY給 angular react vue 框架!node

原理

詳見下面代碼和代碼註釋
// typeScript 數據格式  
// class Node {  
//     title: string;  
//     children: [Node];  
//     fontColor?: string = null;  
//     borderColor?: string = null;  
//     bgColor?: string = null;  
// }  
  
// mack 數據
const treeData = [  
    {  
        title: '張海生',  
  children: [  
            {  
                title: "1.1",  
  children: [  
                    {title: "1.1.1"},  
  {  
                        title: "李1.1.2光",  
  children: [  
                            {title: "張1.1.2.1中"},  
  {title: "1.1.2.2"},  
  {  
                                title: "1.1.2.3",  
  children: [  
                                    {title: "1.1.2.3,1"},  
  {title: "1.1.2.3.2"},  
  {title: "1.1.2.3.3"},  
  {title: "1.1.2.3.4"},  
  {title: "1.1.2.3.5"},  
  ]  
                            },  
  {title: "1.1.2.4"},  
  {title: "1.1.2.5"},  
  ]  
                    },  
  ]  
            },  
  {  
                title: "1.2",  
  children: [  
                    {title: "1.2.1",borderColor:"red",fontColor:"red"},  
  {title: "1.2.2",bgColor:"black",fontColor:"white"},  
  ]  
            },  
  ]  
    }  
];  
  
const nodeFontS = 14; // 字體大小  
const interval = 25; // 節點左右間隔大小  
const padding = 4; // 節點內部padding  
const margin_y = 6; // 節點上下margin  
const fontColor = "#626262"; // 默認字體顏色  
const borderColor = "#55aaee"; // 默認邊框顏色  
const lineColor = "#55aaee"; // 默認連線顏色  
  
const svg = document.getElementById('svgContainer');  
  
let _svgW = 0;  
let _lastNodeN = 0;  
const _nodeH = nodeFontS + padding * 2 + margin_y * 2;  
  
// 樹狀結構數據重構  
reBuildData(treeData, null);  
buildSvg(treeData);  
setSvgContainerSize(_svgW+30, _lastNodeN * _nodeH + 30);  
  
console.log('treeData');  
console.log(treeData);  
console.log(_lastNodeN);  
  
function reBuildData(d, parent) {  //樹狀結構數據重構  
  d.forEach((v, i) => {  
        v.parent = parent;  
  if (v.children && v.children.length > 0) {  
            reBuildData(v.children, v);  
  parentY(v, d.length, i)  
        } else {  
            _lastNodeN = _lastNodeN + 1;  
  v.y = _lastNodeN;  
  parentY(v, d.length, i)  
        }  
    })  
}  
  
function parentY(lastNode, len, i) {  //計算節點的y座標  
  const parent = lastNode.parent;  
  if (len === (i + 1) && !!parent) {  
        const s = parent.children[0].y;  
  parent.y = s + (lastNode.y - s) / 2  
  }  
}  
  
function setSvgContainerSize(w, h) {  //設置svg的寬和高  
  svg.setAttribute("width", w);  
  svg.setAttribute("height", h);  
}  
  
function buildSvg(data) { //生成腦圖svg  
  data.forEach((v) => {  
        buildNode(v);  
  if (!!v.children && v.children.length > 0) {  
            buildSvg(v.children)  
        }  
    })  
}  
  
function buildNode(node) { // 構建腦圖的每一個節點  
  const gTag = document.createElementNS('http://www.w3.org/2000/svg', 'g');  
  const textTag = document.createElementNS('http://www.w3.org/2000/svg', 'text');  
  const text = document.createTextNode(node.title);  
  textTag.appendChild(text);  
  
  gTag.appendChild(textTag)  
    svg.appendChild(gTag);  
  
  textTag.setAttribute("fill", node.fontColor ? node.fontColor : fontColor);  
  const parentx = !!node.parent ? node.parent.x : 0;  
  const parentw = !!node.parent ? node.parent.w : 0;  
  const intervalNow = !!node.parent ? interval : 0;  
  
  const textH = Math.ceil(textTag.getBBox().height);  
  node.w = Math.ceil(textTag.getBBox().width) + padding * 2;  
  node.x = parentx + parentw + intervalNow + 6;  
  gTag.setAttribute('transform', `translate(${node.x + padding},${node.y * (_nodeH) + textH / 2 - 5})`);  
  if ((node.w + node.x) > _svgW) {  
        _svgW = node.w + node.x;  
  }  
    gTag.insertBefore(drawBorder(node), textTag);  
  drawLine(node);  
}  
  
function drawBorder(node){  //畫節點的邊框  
  const fan = document.createElementNS('http://www.w3.org/2000/svg', 'path');  
  fan.setAttribute("d", getBorderD(node));  
  fan.setAttribute('stroke', node.borderColor ? node.borderColor : borderColor);  
  fan.setAttribute("stroke-width", "1");  
  fan.setAttribute("fill", node.bgColor?node.bgColor:"none");  
  return fan;  
}  
function getBorderD(node){  //得到節點的d  
  return `M0,-17h${node.w-8}a5,5,0,0,1,5,5v13a5,5,0,0,1,-5,5h-${node.w-8}a5,5,0,0,1,-5,-5v-13a5,5,0,0,1,5,-5z`  
}  
  
function drawLine(node) {  //爲當前節點連線父級節點  
  if (!node.parent) {  
        return  
  }  
    const fan = document.createElementNS('http://www.w3.org/2000/svg', 'path');  
  svg.appendChild(fan);  
  fan.setAttribute("d", getD(node));  
  fan.setAttribute('stroke', lineColor);  
  fan.setAttribute("stroke-width", "1");  
  fan.setAttribute("fill", "none");  
}  
  
function getD(node) {  // 得到節點的 d線  
  const parent = node.parent;  
  if (parent.y != node.y) {  
        return `M${parent.x + parent.w} ${parent.y * _nodeH} C${parent.x + parent.w + 15} ${parent.y * _nodeH} ${node.x - 15} ${node.y * _nodeH} ${node.x} ${node.y * _nodeH}`;  
  } else {  
        return `M${parent.x + parent.w} ${parent.y * _nodeH} L${node.x} ${node.y * _nodeH}`;  
  }  
}

源碼

複製過來不少字符前面被segmentfault加了轉譯字符,我手動刪除了,能夠會誤刪,要看源碼能夠去我GitHub上clone

github源碼 https://github.com/sure2darli...react

相關文章
相關標籤/搜索