UNPKG

juijs-chart

Version:

SVG-based JUI chart that can be used in the browser and Node.js. Support many types of charts. (Dashboard, Map, Topology, Full 3D)

391 lines (311 loc) 12.6 kB
import jui from '../main.js'; import TreemapBrush from './treemap.js'; jui.use(TreemapBrush); const QuickSort = function (array, isClone) { var compareFunc = null, array = (isClone) ? array.slice(0) : array; function swap(indexA, indexB) { var temp = array[indexA]; array[indexA] = array[indexB]; array[indexB] = temp; } function partition(pivot, left, right) { var storeIndex = left, pivotValue = array[pivot]; swap(pivot, right); for (var v = left; v < right; v++) { if (compareFunc(array[v], pivotValue) || !compareFunc(pivotValue, array[v]) && v % 2 == 1) { swap(v, storeIndex); storeIndex++; } } swap(right, storeIndex); return storeIndex; } this.setCompare = function (func) { compareFunc = func; } this.run = function (left, right) { var pivot = null, newPivot = null; if (typeof left !== 'number') { left = 0; } if (typeof right !== 'number') { right = array.length - 1; } if (left < right) { pivot = left + Math.ceil((right - left) * 0.5); newPivot = partition(pivot, left, right); this.run(left, newPivot - 1); this.run(newPivot + 1, right); } return array; } } export default { name: "chart.brush.flame", extend: "chart.brush.core", component: function () { const _ = jui.include("util.base"); const NodeManager = jui.include("chart.brush.treemap.nodemanager"); const TEXT_MARGIN = 3; /** * @class chart.brush.flame * * @extends chart.brush.core */ const FlameBrush = function () { var self = this, g = null, height = 0, maxHeight = 0, nodes = new NodeManager(), disableOpacity = 1, newData = [], activeDepth = null; function getNodeAndTextOpacity(depth) { return (activeDepth == null) ? 1 : (depth < activeDepth) ? disableOpacity : 1; } function createNodeElement(node, color) { var newColor = self.chart.color(color); var r = self.svg.rect({ fill: newColor, "fill-opacity": getNodeAndTextOpacity(node.depth), stroke: self.chart.theme("flameNodeBorderColor"), "stroke-width": self.chart.theme("flameNodeBorderWidth"), width: node.width, height: node.height, x: node.x, y: node.y, cursor: "pointer" }); // 마우스 오버 효과 r.hover(function () { r.attr({ stroke: newColor }); }, function () { r.attr({ stroke: self.chart.theme("flameNodeBorderColor") }); }); // 노드 공통 이벤트 설정 self.addEvent(r, node); // 노드 엘리먼트 캐싱 node.element = { rect: r }; return r; } function createTextElement(node, color) { if (!_.typeCheck("function", self.brush.format)) { return null; } var newColor = self.chart.color(color), fontSize = self.chart.theme("flameTextFontSize"), startX = node.x; if (self.brush.textAlign == "middle") { startX += node.width / 2; } else if (self.brush.textAlign == "end") { startX += node.width - TEXT_MARGIN; } else { startX += TEXT_MARGIN; } var t = self.chart.text({ "font-size": fontSize, "font-weight": "bold", fill: self.chart.theme("flameTextFontColor"), "fill-opacity": getNodeAndTextOpacity(node.depth), x: startX, y: node.y + (fontSize / 3) + (height / 2), "text-anchor": self.brush.textAlign, cursor: "pointer" }, self.format(node)); // 마우스 오버 효과 t.hover(function () { node.element.rect.attr({ stroke: newColor }); }, function () { node.element.rect.attr({ stroke: self.chart.theme("flameNodeBorderColor") }); }); // 노드 공통 이벤트 설정 self.addEvent(t, node); // 노드 엘리먼트 캐싱 node.element.text = t; return t; } function drawNodeAll(g, node, width, sx) { var color = self.color(0); node.width = width; node.height = height; node.x = sx; // 노드 그리는 위치 설정 if (self.brush.nodeOrient == "bottom") { node.y = maxHeight - (height * node.depth); } else { node.y = height * node.depth; } // 노드 컬러 설정 if (_.typeCheck("function", self.brush.nodeColor)) { color = self.brush.nodeColor.call(self.chart, node); } var r = createNodeElement(node, color), t = createTextElement(node, color); if (self.brush.nodeAlign == "start") { var cStartX = node.x; for (var i = 0; i < node.children.length; i++) { var cNode = node.children[i], cRate = cNode.value / node.value, cWidth = node.width * cRate; drawNodeAll(g, cNode, cWidth, cStartX); cStartX += cWidth; } } else { var cStartX = node.x + node.width; for (var i = node.children.length - 1; i >= 0; i--) { var cNode = node.children[i], cRate = cNode.value / node.value, cWidth = node.width * cRate; cStartX -= cWidth; drawNodeAll(g, cNode, cWidth, cStartX); } } g.append(r); if (t != null) { g.append(t); } } function getMaxDepth(nodes) { var maxDepth = 0; for (var i = 0; i < nodes.length; i++) { maxDepth = Math.max(maxDepth, nodes[i].depth); } return maxDepth; } function createFilteredNodes(activeNode) { setCacheParents(activeNode, activeNode.value); setCacheChildren(activeNode); sortingCacheNodes(); var tmpData = createIndexData(activeNode), tmpNodes = new NodeManager(); for (var i = 0; i < tmpData.length; i++) { var d = tmpData[i]; tmpNodes.insertNode(d.index, { text: d.text, value: d.value, x: 0, y: 0, width: 0, height: 0 }); } // 필터링 된 노드 캐싱 self.axis.cacheNodes = tmpNodes; return tmpNodes.getNode()[0]; } function setCacheParents(node, value) { if (node.depth > 0) { node.value = value; newData.push(node); if (node.parent) { setCacheParents(node.parent, value); } } } function setCacheChildren(node) { for (var i = 0; i < node.children.length; i++) { var cNode = node.children[i]; newData.push(cNode); if (cNode.children.length > 0) { setCacheChildren(cNode); } } } function sortingCacheNodes() { var qs = new QuickSort(newData); qs.setCompare(function (a, b) { return (a.depth < b.depth) ? true : false; }); qs.run(); } function createIndexData(node) { var tmpData = [], index = ""; for (var i = 0; i < newData.length; i++) { if (index == "") { index = "0"; } else { index += ".0"; } if (newData[i].depth < node.depth) { tmpData.push({ index: index, text: newData[i].text, value: newData[i].value }); } else { createChildIndexData(node, index, tmpData); break; } } return tmpData; } function createChildIndexData(node, index, result) { result.push({ index: index, value: node.value, text: node.text }); for (var i = 0; i < node.children.length; i++) { var cNode = node.children[i]; createChildIndexData(cNode, index + "." + i, result); } } this.drawBefore = function () { g = this.svg.group(); for (var i = 0; i < this.axis.data.length; i++) { var d = this.axis.data[i], k = this.getValue(d, "index"); nodes.insertNode(k, { text: "" + this.getValue(d, "text", ""), value: this.getValue(d, "value", 0), x: this.getValue(d, "x", 0), y: this.getValue(d, "y", 0), width: this.getValue(d, "width", 0), height: this.getValue(d, "height", 0) }); } var maxDepth = (this.brush.maxDepth == null) ? getMaxDepth(nodes.getNodeAll()) : this.brush.maxDepth; height = this.axis.area("height") / maxDepth; maxHeight = this.axis.area("height"); // 비활성화 노드 투명도 disableOpacity = this.chart.theme("flameDisableBackgroundOpacity"); } this.draw = function () { var area = this.axis.area(), root = nodes.getNode()[0], activeIndex = this.brush.activeIndex; if (root) { // 액티브 노드가 있을 경우, 노드 재정의 if (_.typeCheck("string", activeIndex)) { var activeNode = nodes.getNode(activeIndex); if (activeNode == null) { activeNode = this.axis.cacheNodes.getNode(activeIndex); } root = createFilteredNodes(activeNode); activeDepth = activeNode.depth; } drawNodeAll(g, root, area.width, area.x); } return g; } } FlameBrush.setup = function () { return { maxDepth: null, nodeOrient: "bottom", nodeAlign: "end", textAlign: "start", nodeColor: null, activeIndex: null, clip: false, format: null }; } return FlameBrush; } }