blob: 6e7ba735f50d638740a529164c263c45fcaeebb0 [file] [log] [blame]
/**
* @file This file used to draw tree view
*/
import * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import SymbolClz from '../helper/Symbol';
import { radialCoordinate } from './layoutHelper';
import * as echarts from '../../echarts';
export default echarts.extendChartView({
type: 'tree',
/**
* Init the chart
* @override
* @param {module:echarts/model/Global} ecModel
* @param {module:echarts/ExtensionAPI} api
*/
init: function (ecModel, api) {
/**
* @private
* @type {module:echarts/data/Tree}
*/
this._oldTree;
/**
* @private
* @type {module:zrender/container/Group}
*/
this._mainGroup = new graphic.Group();
this.group.add(this._mainGroup);
},
render: function (seriesModel, ecModel, api, payload) {
var data = seriesModel.getData();
var layoutInfo = seriesModel.layoutInfo;
var group = this._mainGroup;
var layout = seriesModel.get('layout');
if (layout === 'radial') {
group.attr('position', [layoutInfo.x + layoutInfo.width / 2, layoutInfo.y + layoutInfo.height / 2]);
} else {
group.attr('position', [layoutInfo.x, layoutInfo.y]);
}
var oldData = this._data;
var seriesScope = {
expandAndCollapse: seriesModel.get('expandAndCollapse'),
layout: layout,
orient: seriesModel.get('orient'),
curvature: seriesModel.get('lineStyle.normal.curveness'),
symbolRotate: seriesModel.get('symbolRotate'),
symbolOffset: seriesModel.get('symbolOffset'),
hoverAnimation: seriesModel.get('hoverAnimation'),
useNameLabel: true,
fadeIn: true
};
data.diff(oldData).add(function (newIdx) {
if (symbolNeedsDraw(data, newIdx)) {
// create node and edge
updateNode(data, newIdx, null, group, seriesModel, seriesScope);
}
}).update(function (newIdx, oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
if (!symbolNeedsDraw(data, newIdx)) {
symbolEl && removeNode(data, newIdx, symbolEl, group, seriesModel, seriesScope);
return;
} // update node and edge
updateNode(data, newIdx, symbolEl, group, seriesModel, seriesScope);
}).remove(function (oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
removeNode(data, oldIdx, symbolEl, group, seriesModel, seriesScope);
}).execute();
if (seriesScope.expandAndCollapse === true) {
data.eachItemGraphicEl(function (el, dataIndex) {
el.off('click').on('click', function () {
api.dispatchAction({
type: 'treeExpandAndCollapse',
seriesId: seriesModel.id,
dataIndex: dataIndex
});
});
});
}
this._data = data;
},
dispose: function () {},
remove: function () {
this._mainGroup.removeAll();
this._data = null;
}
});
function symbolNeedsDraw(data, dataIndex) {
var layout = data.getItemLayout(dataIndex);
return layout && !isNaN(layout.x) && !isNaN(layout.y) && data.getItemVisual(dataIndex, 'symbol') !== 'none';
}
function getTreeNodeStyle(node, itemModel, seriesScope) {
seriesScope.itemModel = itemModel;
seriesScope.itemStyle = itemModel.getModel('itemStyle.normal').getItemStyle();
seriesScope.hoverItemStyle = itemModel.getModel('itemStyle.emphasis').getItemStyle();
seriesScope.lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle();
seriesScope.labelModel = itemModel.getModel('label.normal');
seriesScope.hoverLabelModel = itemModel.getModel('label.emphasis');
if (node.isExpand === false && node.children.length !== 0) {
seriesScope.symbolInnerColor = seriesScope.itemStyle.fill;
} else {
seriesScope.symbolInnerColor = '#fff';
}
return seriesScope;
}
function updateNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) {
var isInit = !symbolEl;
var node = data.tree.getNodeByDataIndex(dataIndex);
var itemModel = node.getModel();
var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope);
var virtualRoot = data.tree.root;
var source = node.parentNode === virtualRoot ? node : node.parentNode || node;
var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex);
var sourceLayout = source.getLayout();
var sourceOldLayout = sourceSymbolEl ? {
x: sourceSymbolEl.position[0],
y: sourceSymbolEl.position[1],
rawX: sourceSymbolEl.__radialOldRawX,
rawY: sourceSymbolEl.__radialOldRawY
} : sourceLayout;
var targetLayout = node.getLayout();
if (isInit) {
symbolEl = new SymbolClz(data, dataIndex, seriesScope);
symbolEl.attr('position', [sourceOldLayout.x, sourceOldLayout.y]);
} else {
symbolEl.updateData(data, dataIndex, seriesScope);
}
symbolEl.__radialOldRawX = symbolEl.__radialRawX;
symbolEl.__radialOldRawY = symbolEl.__radialRawY;
symbolEl.__radialRawX = targetLayout.rawX;
symbolEl.__radialRawY = targetLayout.rawY;
group.add(symbolEl);
data.setItemGraphicEl(dataIndex, symbolEl);
graphic.updateProps(symbolEl, {
position: [targetLayout.x, targetLayout.y]
}, seriesModel);
var symbolPath = symbolEl.getSymbolPath();
if (seriesScope.layout === 'radial') {
var realRoot = virtualRoot.children[0];
var rootLayout = realRoot.getLayout();
var length = realRoot.children.length;
var rad;
var isLeft;
if (targetLayout.x === rootLayout.x && node.isExpand === true) {
var center = {};
center.x = (realRoot.children[0].getLayout().x + realRoot.children[length - 1].getLayout().x) / 2;
center.y = (realRoot.children[0].getLayout().y + realRoot.children[length - 1].getLayout().y) / 2;
rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x);
if (rad < 0) {
rad = Math.PI * 2 + rad;
}
isLeft = center.x < rootLayout.x;
if (isLeft) {
rad = rad - Math.PI;
}
} else {
rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x);
if (rad < 0) {
rad = Math.PI * 2 + rad;
}
if (node.children.length === 0 || node.children.length !== 0 && node.isExpand === false) {
isLeft = targetLayout.x < rootLayout.x;
if (isLeft) {
rad = rad - Math.PI;
}
} else {
isLeft = targetLayout.x > rootLayout.x;
if (!isLeft) {
rad = rad - Math.PI;
}
}
}
var textPosition = isLeft ? 'left' : 'right';
symbolPath.setStyle({
textPosition: textPosition,
textRotation: -rad,
textOrigin: 'center',
verticalAlign: 'middle'
});
}
if (node.parentNode && node.parentNode !== virtualRoot) {
var edge = symbolEl.__edge;
if (!edge) {
edge = symbolEl.__edge = new graphic.BezierCurve({
shape: getEdgeShape(seriesScope, sourceOldLayout, sourceOldLayout),
style: zrUtil.defaults({
opacity: 0
}, seriesScope.lineStyle)
});
}
graphic.updateProps(edge, {
shape: getEdgeShape(seriesScope, sourceLayout, targetLayout),
style: {
opacity: 1
}
}, seriesModel);
group.add(edge);
}
}
function removeNode(data, dataIndex, symbolEl, group, seriesModel, seriesScope) {
var node = data.tree.getNodeByDataIndex(dataIndex);
var virtualRoot = data.tree.root;
var itemModel = node.getModel();
var seriesScope = getTreeNodeStyle(node, itemModel, seriesScope);
var source = node.parentNode === virtualRoot ? node : node.parentNode || node;
var sourceLayout;
while (sourceLayout = source.getLayout(), sourceLayout == null) {
source = source.parentNode === virtualRoot ? source : source.parentNode || source;
}
graphic.updateProps(symbolEl, {
position: [sourceLayout.x + 1, sourceLayout.y + 1]
}, seriesModel, function () {
group.remove(symbolEl);
data.setItemGraphicEl(dataIndex, null);
});
symbolEl.fadeOut(null, {
keepLabel: true
});
var edge = symbolEl.__edge;
if (edge) {
graphic.updateProps(edge, {
shape: getEdgeShape(seriesScope, sourceLayout, sourceLayout),
style: {
opacity: 0
}
}, seriesModel, function () {
group.remove(edge);
});
}
}
function getEdgeShape(seriesScope, sourceLayout, targetLayout) {
var cpx1;
var cpy1;
var cpx2;
var cpy2;
var orient = seriesScope.orient;
if (seriesScope.layout === 'radial') {
var x1 = sourceLayout.rawX;
var y1 = sourceLayout.rawY;
var x2 = targetLayout.rawX;
var y2 = targetLayout.rawY;
var radialCoor1 = radialCoordinate(x1, y1);
var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * seriesScope.curvature);
var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * seriesScope.curvature);
var radialCoor4 = radialCoordinate(x2, y2);
return {
x1: radialCoor1.x,
y1: radialCoor1.y,
x2: radialCoor4.x,
y2: radialCoor4.y,
cpx1: radialCoor2.x,
cpy1: radialCoor2.y,
cpx2: radialCoor3.x,
cpy2: radialCoor3.y
};
} else {
var x1 = sourceLayout.x;
var y1 = sourceLayout.y;
var x2 = targetLayout.x;
var y2 = targetLayout.y;
if (orient === 'horizontal') {
cpx1 = x1 + (x2 - x1) * seriesScope.curvature;
cpy1 = y1;
cpx2 = x2 + (x1 - x2) * seriesScope.curvature;
cpy2 = y2;
}
if (orient === 'vertical') {
cpx1 = x1;
cpy1 = y1 + (y2 - y1) * seriesScope.curvature;
cpx2 = x2;
cpy2 = y2 + (y1 - y2) * seriesScope.curvature;
}
return {
x1: x1,
y1: y1,
x2: x2,
y2: y2,
cpx1: cpx1,
cpy1: cpy1,
cpx2: cpx2,
cpy2: cpy2
};
}
}