できたもの
できること
- textarea内に記載された箇条書きに従ってマインドマップ(SVG)をリアルタイムに生成する
- 依存ライブラリがjqueryだけなので、ブラウザさえあれば特別なソフトなしで使用可能
できないこと
実装上のポイント
- jsから動的にSVG要素を作るときは、createElementではなくcreatElementNSを使う
- SVGで自動改行するテキストボックスを作るにはforeignObject要素の子要素としてdivを配置
- その際foreignObjectはSVG要素の直下に置く。
- foreignObject配下にdivを作るときもcreateElementNSを使っておいたほうがよさそう
var svgNS = "http://www.w3.org/2000/svg";
var htmlNS = "http://www.w3.org/1999/xhtml"
var foreignElement = document.createElementNS(svgNS, "foreignObject");
foreignElement.setAttribute("width", nodeWidth);
foreignElement.setAttribute("height", nodeHeight);
foreignElement.setAttribute("x", x);
foreignElement.setAttribute("y", y);
var innerElement = document.createElementNS(htmlNS, "div");
innerElement.innerText = text;
document.getElementById("map").appendChild(foreignElement);
foreignElement.appendChild(innerElement);
コード
<html>
<head>
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
</head>
<style>
</style>
<body>
<div>
<div style="float:left">
<textarea id="text" name="text" rows="20" cols="40"></textarea>
</div>
<div style="float:left;">
<svg id="map" width="700" height="500"></svg>
</div>
</div>
<script type="text/javascript">
var svgNS = "http://www.w3.org/2000/svg";
var htmlNS = "http://www.w3.org/1999/xhtml"
var inputText = "";
$("#text").keyup(function(){
if(inputText != $("#text").val())
{
inputText = $("#text").val()
drawMap(inputText);
}
})
var nodeWidth = 100;
var nodeHeight = 50;
var xMargin = 50;
var yMargin = 20;
function drawMap(text)
{
$("#map").empty();
console.log(text)
nodeArray = parseText(text);
nodeArray = decideNodePosition(nodeArray);
console.log(nodeArray);
for(var i = 0; i < nodeArray.length; i++)
{
drawSingleNode(nodeArray[i].text, nodeArray[i].x, nodeArray[i].y)
var children = nodeArray[i].children;
for(var j = 0; j < children.length; j++)
{
connectNodes(nodeArray[i], children[j]);
}
}
}
function decideNodePosition(nodeArray)
{
var leafCounter = 0;
for(var i = 0; i < nodeArray.length; i++)
{
var level = nodeArray[i].level;
nodeArray[i]["x"] = level*(nodeWidth + xMargin)
if(nodeArray[i].children.length == 0)
{
nodeArray[i]["y"] = leafCounter*(nodeHeight + yMargin)
leafCounter++;
}
}
for(var i = nodeArray.length - 1; i >= 0; i--)
{
if(nodeArray[i].children.length > 0)
{
var y = 0;
var middleNodeID = Math.floor(nodeArray[i].children.length/2);
if(nodeArray[i].children.length % 2 == 1)
{
y = nodeArray[i].children[middleNodeID].y;
}
else
{
var y1 = nodeArray[i].children[middleNodeID].y;
var y2 = nodeArray[i].children[middleNodeID - 1].y;
y = (y1 + y2)/2;
}
nodeArray[i]["y"] = y;
}
}
return nodeArray
}
function drawSingleNode(text, x, y)
{
var rectElement = document.createElementNS(svgNS, "rect");
rectElement.setAttribute("width", nodeWidth);
rectElement.setAttribute("height", nodeHeight);
rectElement.setAttribute("x", x);
rectElement.setAttribute("y", y);
rectElement.setAttribute("fill", "white");
rectElement.setAttribute("stroke", "black");
var foreignElement = document.createElementNS(svgNS, "foreignObject");
foreignElement.setAttribute("width", nodeWidth);
foreignElement.setAttribute("height", nodeHeight);
foreignElement.setAttribute("x", x);
foreignElement.setAttribute("y", y);
var innerElement = document.createElementNS(htmlNS, "div");
innerElement.innerText = text;
innerElement.style["padding"] = "0 5 0 5";
document.getElementById("map").appendChild(rectElement);
document.getElementById("map").appendChild(foreignElement);
foreignElement.appendChild(innerElement);
}
function connectNodes(fromNode, toNode)
{
var lineElement = document.createElementNS(svgNS, "line");
lineElement.setAttribute("x1", fromNode.x + nodeWidth);
lineElement.setAttribute("y1", fromNode.y + nodeHeight/2);
lineElement.setAttribute("x2", toNode.x);
lineElement.setAttribute("y2", toNode.y + nodeHeight/2);
lineElement.setAttribute("stroke", "black");
document.getElementById("map").appendChild(lineElement);
}
function parseText(text)
{
textArray = text.split("\n")
mapObject = new Object();
nodeArray = [];
levelArray = [];
levelArray[0] = null;
for(var i = 0; i < textArray.length; i++)
{
var level = 0;
var text = textArray[i];
for(var j = 0; j < text.length; j++)
{
if(text.startsWith("*"))
{
text = text.substring(1);
level++;
}
else
{
break;
}
}
text = text.trim();
var node = {id: i.toString(), text: text, level: level, parent: levelArray[level]}
nodeArray.push(node)
levelArray[level + 1] = node;
}
childrenMap = {};
for(var i = nodeArray.length - 1; i >= 0; i--)
{
if(nodeArray[i].id in childrenMap)
{
nodeArray[i]["children"] = childrenMap[nodeArray[i].id];
}
else
{
nodeArray[i]["children"] = [];
}
if(nodeArray[i].parent != null)
{
if(!(nodeArray[i].parent.id in childrenMap))
{
childrenMap[nodeArray[i].parent.id] = []
}
childrenMap[nodeArray[i].parent.id].push(nodeArray[i]);
}
}
return nodeArray;
}
</script>
</body>
</html>