mind map 思惟導圖

 

 

 

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Mind Map</title>
    <meta name="description"
        content="A mind map editor, showing how subtrees can be moved, copied, deleted, and laid out." />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Copyright 1998-2019 by Northwoods Software Corporation. -->

    <script src="https://unpkg.com/gojs/release/go-debug.js"></script>
    <script id="code">
        function init() {
            if (window.goSamples) goSamples(); // 這些樣本的初始化-您無需調用此
            var $ = go.GraphObject.make;

            myDiagram =
                $(go.Diagram, "myDiagramDiv", {
                    "maxSelectionCount": 1, // 用戶一次只能選擇一個節點
                    "initialContentAlignment": go.Spot.Center, // 畫布內居中顯示
                    "allowZoom": true, //畫布是否能夠縮放
                    // 當用戶拖動節點時,還要從該節點開始移動/複製/刪除整個子樹
                    "commandHandler.copiesTree": true,
                    "commandHandler.copiesParentKey": true,
                    "commandHandler.deletesTree": true,
                    "draggingTool.dragsTree": true, //拖拽?
                    "undoManager.isEnabled": false, //  支持 Ctrl-Z 和 Ctrl-Y 操做
                    //禁止橫豎拖動和鼠標滾動,流程圖總體的拖動
                    "allowHorizontalScroll": false,
                    "allowVerticalScroll": false,
                    "isReadOnly": true, // 只讀
                });

            // 當文檔被修改時,在標題中添加「*」並啓用「Save」按鈕
            myDiagram.addDiagramListener("Modified", function (e) {
                var button = document.getElementById("SaveButton");
                if (button) button.disabled = !myDiagram.isModified;
                var idx = document.title.indexOf("*");
                if (myDiagram.isModified) {
                    if (idx < 0) document.title += "*";
                } else {
                    if (idx >= 0) document.title = document.title.substr(0, idx);
                }
            });

            // 一個節點由一些文本和一條線形狀組成
            myDiagram.nodeTemplate =
                $(go.Node, "Vertical", {
                        selectionObjectName: "TEXT"
                    },
                    $(go.TextBlock, {
                            name: "TEXT",
                            minSize: new go.Size(30, 15),
                            editable: false //是否容許雙擊編輯
                        },
                        // 不只要記住文本字符串,還要記住節點數據中的比例和字體
                        new go.Binding("text", "text").makeTwoWay(),
                        new go.Binding("scale", "scale").makeTwoWay(),
                        new go.Binding("font", "font").makeTwoWay()),
                    $(go.Shape, "LineH", {
                            stretch: go.GraphObject.Horizontal,
                            strokeWidth: 3,
                            height: 3,
                            // 這條線的形狀是端口——鏈接的是什麼
                            portId: "",
                            fromSpot: go.Spot.LeftRightSides,
                            toSpot: go.Spot.LeftRightSides
                        },
                        new go.Binding("stroke", "brush"),
                        // 確保連接來自正確的方向,並適當地發送出去
                        new go.Binding("fromSpot", "dir", function (d) {
                            return spotConverter(d, true);
                        }),
                        new go.Binding("toSpot", "dir", function (d) {
                            return spotConverter(d, false);
                        })),
                    // 記住節點數據中每一個節點的位置
                    new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
                    // 確保文本按照指望的方向「增加」
                    new go.Binding("locationSpot", "dir", function (d) {
                        return spotConverter(d, false);
                    })
                );

            // 選中的節點顯示用於添加子節點的按鈕
            myDiagram.nodeTemplate.selectionAdornmentTemplate =
                $(go.Adornment, "Spot",
                    $(go.Panel, "Auto",
                        // 此裝飾在選定節點周圍具備矩形藍色形狀
                        $(go.Shape, {
                            fill: null,
                            stroke: "dodgerblue",
                            strokeWidth: 3
                        }),
                        $(go.Placeholder, {
                            margin: new go.Margin(4, 4, 0, 4)
                        })
                    ),
                    // 所選節點右側的刪除按鈕
                    $("Button", {
                            alignment: go.Spot.Right,
                            alignmentFocus: go.Spot.Left,
                            click: function (e, obj) {
                                e.diagram.commandHandler.deleteSelection();
                                layoutAll(); // 刪除後整理佈局
                            }
                            // 定義裝飾中此按鈕的單擊行爲
                        },
                        $(go.TextBlock, "-", // 按鈕的內容
                            {
                                font: "bold 8pt sans-serif"
                            })
                    ),
                    // 所選節點右側的新增按鈕
                    $("Button", {
                            alignment: go.Spot.Right,
                            alignmentFocus: go.Spot.Right,
                            click: addNodeAndLink // 定義裝飾中此按鈕的單擊行爲
                        },
                        $(go.TextBlock, "+", // 按鈕的內容
                            {
                                font: "bold 8pt sans-serif"
                            })
                    )
                );

            // 上下文菜單容許用戶更改字體大小和粗細,
            // 並從該節點開始執行有限的樹形佈局
            myDiagram.nodeTemplate.contextMenu =
                $("ContextMenu",
                    $("ContextMenuButton",
                        $(go.TextBlock, "Bigger"), {
                            click: function (e, obj) {
                                changeTextSize(obj, 1.1);
                                layoutAll();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Smaller"), {
                            click: function (e, obj) {
                                changeTextSize(obj, 1 / 1.1);
                                layoutAll();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Bold/Normal"), {
                            click: function (e, obj) {
                                toggleTextWeight(obj);
                                layoutAll();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Copy"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.copySelection();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Delete"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.deleteSelection();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Undo"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.undo();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Redo"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.redo();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Layout"), {
                            click: function (e, obj) {
                                var adorn = obj.part;
                                adorn.diagram.startTransaction("Subtree Layout");
                                layoutTree(adorn.adornedPart);
                                adorn.diagram.commitTransaction("Subtree Layout");
                            }
                        }
                    )
                );

            // 鏈接線顏色
            myDiagram.linkTemplate =
                $(go.Link, {
                        curve: go.Link.Bezier,
                        fromShortLength: -2,
                        toShortLength: -2,
                        selectable: false
                    },
                    $(go.Shape, {
                            strokeWidth: 3
                        },
                        new go.Binding("stroke", "toNode", function (n) {
                            if (n.data.brush) return n.data.brush;
                            return "black";
                        }).ofObject())
                );

            // 該圖的上下文菜單僅顯示常規功能的命令
            myDiagram.contextMenu =
                $("ContextMenu",
                    $("ContextMenuButton",
                        $(go.TextBlock, "Paste"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.pasteSelection(e.diagram.toolManager.contextMenuTool
                                    .mouseDownPoint);
                            }
                        },
                        new go.Binding("visible", "", function (o) {
                            return o.diagram && o.diagram.commandHandler.canPasteSelection(o.diagram.toolManager
                                .contextMenuTool.mouseDownPoint);
                        }).ofObject()),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Undo"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.undo();
                            }
                        },
                        new go.Binding("visible", "", function (o) {
                            return o.diagram && o.diagram.commandHandler.canUndo();
                        }).ofObject()),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Redo"), {
                            click: function (e, obj) {
                                e.diagram.commandHandler.redo();
                            }
                        },
                        new go.Binding("visible", "", function (o) {
                            return o.diagram && o.diagram.commandHandler.canRedo();
                        }).ofObject()),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Save"), {
                            click: function (e, obj) {
                                save();
                            }
                        }),
                    $("ContextMenuButton",
                        $(go.TextBlock, "Load"), {
                            click: function (e, obj) {
                                load();
                            }
                        })
                );

            myDiagram.addDiagramListener("SelectionMoved", function (e) {
                var rootX = myDiagram.findNodeForKey(0).location.x;
                myDiagram.selection.each(function (node) {
                    if (node.data.parent !== 0) return; // 只考慮鏈接到根的節點
                    var nodeX = node.location.x;
                    if (rootX < nodeX && node.data.dir !== "right") {
                        updateNodeDirection(node, "right");
                    } else if (rootX > nodeX && node.data.dir !== "left") {
                        updateNodeDirection(node, "left");
                    }
                    layoutTree(node);
                });
            });

            // 使用「 mySavedModel」文本區域中保存的JSON格式數據讀取預約義的圖形
            load();
        }

        function spotConverter(dir, from) {
            if (dir === "left") {
                return (from ? go.Spot.Left : go.Spot.Right);
            } else {
                return (from ? go.Spot.Right : go.Spot.Left);
            }
        }

        function changeTextSize(obj, factor) {
            var adorn = obj.part;
            adorn.diagram.startTransaction("Change Text Size");
            var node = adorn.adornedPart;
            var tb = node.findObject("TEXT");
            tb.scale *= factor;
            adorn.diagram.commitTransaction("Change Text Size");
        }

        function toggleTextWeight(obj) {
            var adorn = obj.part;
            adorn.diagram.startTransaction("Change Text Weight");
            var node = adorn.adornedPart;
            var tb = node.findObject("TEXT");
            // 假定「粗體」在字體說明符的開頭
            var idx = tb.font.indexOf("bold");
            if (idx < 0) {
                tb.font = "bold " + tb.font;
            } else {
                tb.font = tb.font.substr(idx + 5);
            }
            adorn.diagram.commitTransaction("Change Text Weight");
        }

        function updateNodeDirection(node, dir) {
            myDiagram.model.setDataProperty(node.data, "dir", dir);
            // 遞歸更新子節點的方向
            var chl = node
                .findTreeChildrenNodes(); // 爲咱們提供了與此特定節點相關的子節點的迭代器
            while (chl.next()) {
                updateNodeDirection(chl.value, dir);
            }
        }

        function addNodeAndLink(e, obj) {
            var adorn = obj.part;
            var diagram = adorn.diagram;
            diagram.startTransaction("Add Node");
            var oldnode = adorn.adornedPart;
            var olddata = oldnode.data;
            // 將筆刷和方向複製到新的節點數據
            var newdata = {
                text: "idea",
                brush: olddata.brush,
                dir: olddata.dir,
                parent: olddata.key
            };
            diagram.model.addNodeData(newdata);
            layoutTree(oldnode);
            diagram.commitTransaction("Add Node");

            // 若是新節點不在屏幕上,請滾動圖表以顯示新節點
            var newnode = diagram.findNodeForData(newdata);
            if (newnode !== null) diagram.scrollToRect(newnode.actualBounds);
            layoutAll(); //添加完畢 整理佈局
        }

        function layoutTree(node) {
            if (node.data.key === 0) { // 添加到根?
                layoutAll(); // 整理佈局
            } else { // 不然,僅佈局今後父節點開始的子樹
                var parts = node.findTreeParts();
                layoutAngle(parts, node.data.dir === "left" ? 180 : 0);
            }
        }

        function layoutAngle(parts, angle) {
            var layout = go.GraphObject.make(go.TreeLayout, {
                angle: angle,
                arrangement: go.TreeLayout.ArrangementFixedRoots,
                nodeSpacing: 5,
                layerSpacing: 20,
                setsPortSpot: false, // 不要設置端口點,由於咱們正在使用spotConverter函數進行管理
                setsChildPortSpot: false
            });
            layout.doLayout(parts);
        }

        function layoutAll() {
            var root = myDiagram.findNodeForKey(0);
            if (root === null) return;
            myDiagram.startTransaction("Layout");
            // 將節點和連接分爲兩個集合
            var rightward = new go.Set( /*go.Part*/ );
            var leftward = new go.Set( /*go.Part*/ );
            root.findLinksConnected().each(function (link) {
                var child = link.toNode;
                if (child.data.dir === "left") {
                    leftward.add(root); // 根節點在兩個集合中
                    leftward.add(link);
                    leftward.addAll(child.findTreeParts());
                } else {
                    rightward.add(root); // 根節點在兩個集合中
                    rightward.add(link);
                    rightward.addAll(child.findTreeParts());
                }
            });
            // 進行一個佈局,而後進行另外一個佈局而不移動共享的根節點
            layoutAngle(rightward, 0);
            layoutAngle(leftward, 180);
            myDiagram.commitTransaction("Layout");
        }

        // 以JSON格式顯示圖的模型
        function save() {
            document.getElementById("mySavedModel").value = myDiagram.model.toJson();
            myDiagram.isModified = false;
        }

        function load() {
            myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
        }
    </script>
</head>

<body onload="init()">
    <div id="sample">
        <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:300px;"></div>
        <p>
            A mind map is a kind of spider diagram that organizes information around a central concept, with connecting
            branches.
        </p>
        <p>
            The layout is controlled by moving the nodes closest to the tree's root node.
            When one of these nodes is moved horizontally to the other side of the root,
            all of its children will be sent to <a>Layout.doLayout</a> with a new direction,
            causing text to always be moved outwards from the root. The <b>spotConverter</b> function is used to manage
            <a>GraphObject.fromSpot</a> and <a>GraphObject.toSpot</a> for nodes manually, so the
            <a>TreeLayout.setsPortSpot</a> and <a>TreeLayout.setsChildPortSpot</a>
            properties are set to false so that laying out the diagram will not overwrite the values.
        </p>
        <p>
            When a node is deleted the <a>CommandHandler.deletesTree</a> property ensures that
            all of its children are deleted with it. When a node is dragged the <a>DraggingTool.dragsTree</a>
            property ensures that all its children are dragged with it.
            Both of these are set during the the Diagram's initalization.
        </p>
        <p>
            Node templates also have a <a>Part.selectionAdornmentTemplate</a> defined to allow for new nodes to be
            created and a <a>GraphObject.contextMenu</a> with additional commands.
        </p>

        <button id="SaveButton" onclick="save()">Save</button>
        <button onclick="load()">Load</button>
        <button onclick="layoutAll()">Layout</button>
        Diagram Model saved in JSON format:
        <br />
        <textarea id="mySavedModel" style="width:100%;height:400px">
{ "class": "TreeModel",
  "nodeDataArray": [ 
{"key":0, "text":"Mind Map", "loc":"-400 9"},
{"key":1, "parent":0, "text":"Getting more time", "brush":"skyblue", "dir":"right", "loc":"-315.47607421875 -14.134118652343766"},
{"key":11, "parent":1, "text":"Wake up early", "brush":"skyblue", "dir":"right", "loc":"-180.62158203125 -41.64627685546877"},
{"key":12, "parent":1, "text":"Delegate", "brush":"skyblue", "dir":"right", "loc":"-180.62158203125 -14.13411865234377"},
{"key":13, "parent":1, "text":"Simplify", "brush":"skyblue", "dir":"right", "loc":"-180.62158203125 13.378039550781233"},
{"key":2, "parent":0, "text":"More effective use", "brush":"darkseagreen", "dir":"right", "loc":"-315.47607421875 54.64627685546874"},
{"key":21, "parent":2, "text":"Planning", "brush":"darkseagreen", "dir":"right", "loc":"-180.28515625 40.890197753906236"},
{"key":211, "parent":21, "text":"Priorities", "brush":"darkseagreen", "dir":"right", "loc":"-105.87939453125 27.134118652343737"},
{"key":212, "parent":21, "text":"Ways to focus", "brush":"darkseagreen", "dir":"right", "loc":"-105.87939453125 54.646276855468734"},
{"key":22, "parent":2, "text":"Goals", "brush":"darkseagreen", "dir":"right", "loc":"-180.28515625 68.40235595703125"}
 ]}
  </textarea>
    </div>
</body>

</html>
相關文章
相關標籤/搜索