349 lines
13 KiB
Plaintext
349 lines
13 KiB
Plaintext
define(function(require) {
|
||
|
||
var SeriesModel = require('../../model/Series');
|
||
var Tree = require('../../data/Tree');
|
||
var zrUtil = require('zrender/core/util');
|
||
var Model = require('../../model/Model');
|
||
var formatUtil = require('../../util/format');
|
||
var encodeHTML = formatUtil.encodeHTML;
|
||
var addCommas = formatUtil.addCommas;
|
||
|
||
|
||
return SeriesModel.extend({
|
||
|
||
type: 'series.treemap',
|
||
|
||
dependencies: ['grid', 'polar'],
|
||
|
||
/**
|
||
* @type {module:echarts/data/Tree~Node}
|
||
*/
|
||
_viewRoot: null,
|
||
|
||
defaultOption: {
|
||
// center: ['50%', '50%'], // not supported in ec3.
|
||
// size: ['80%', '80%'], // deprecated, compatible with ec2.
|
||
left: 'center',
|
||
top: 'middle',
|
||
right: null,
|
||
bottom: null,
|
||
width: '80%',
|
||
height: '80%',
|
||
sort: true, // Can be null or false or true
|
||
// (order by desc default, asc not supported yet (strange effect))
|
||
clipWindow: 'origin', // Size of clipped window when zooming. 'origin' or 'fullscreen'
|
||
squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
|
||
leafDepth: null, // Nodes on depth from root are regarded as leaves.
|
||
// Count from zero (zero represents only view root).
|
||
drillDownIcon: '▶', // Use html character temporarily because it is complicated
|
||
// to align specialized icon. ▷▶❒❐▼✚
|
||
visualDimension: 0, // Can be 0, 1, 2, 3.
|
||
zoomToNodeRatio: 0.32 * 0.32, // Be effective when using zoomToNode. Specify the proportion of the
|
||
// target node area in the view area.
|
||
roam: true, // true, false, 'scale' or 'zoom', 'move'.
|
||
nodeClick: 'zoomToNode', // Leaf node click behaviour: 'zoomToNode', 'link', false.
|
||
// If leafDepth is set and clicking a node which has children but
|
||
// be on left depth, the behaviour would be changing root. Otherwise
|
||
// use behavious defined above.
|
||
animation: true,
|
||
animationDurationUpdate: 900,
|
||
animationEasing: 'quinticInOut',
|
||
breadcrumb: {
|
||
show: true,
|
||
height: 22,
|
||
left: 'center',
|
||
top: 'bottom',
|
||
// right
|
||
// bottom
|
||
emptyItemWidth: 25, // Width of empty node.
|
||
itemStyle: {
|
||
normal: {
|
||
color: 'rgba(0,0,0,0.7)', //'#5793f3',
|
||
borderColor: 'rgba(255,255,255,0.7)',
|
||
borderWidth: 1,
|
||
shadowColor: 'rgba(150,150,150,1)',
|
||
shadowBlur: 3,
|
||
shadowOffsetX: 0,
|
||
shadowOffsetY: 0,
|
||
textStyle: {
|
||
color: '#fff'
|
||
}
|
||
},
|
||
emphasis: {
|
||
textStyle: {}
|
||
}
|
||
}
|
||
},
|
||
label: {
|
||
normal: {
|
||
show: true,
|
||
position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ...
|
||
textStyle: {
|
||
color: '#fff',
|
||
ellipsis: true
|
||
}
|
||
}
|
||
},
|
||
itemStyle: {
|
||
normal: {
|
||
color: null, // Can be 'none' if not necessary.
|
||
colorAlpha: null, // Can be 'none' if not necessary.
|
||
colorSaturation: null, // Can be 'none' if not necessary.
|
||
borderWidth: 0,
|
||
gapWidth: 0,
|
||
borderColor: '#fff',
|
||
borderColorSaturation: null // If specified, borderColor will be ineffective, and the
|
||
// border color is evaluated by color of current node and
|
||
// borderColorSaturation.
|
||
},
|
||
emphasis: {
|
||
|
||
}
|
||
},
|
||
color: 'none', // Array. Specify color list of each level.
|
||
// level[0].color would be global color list.
|
||
colorAlpha: null, // Array. Specify color alpha range of each level, like [0.2, 0.8]
|
||
colorSaturation: null, // Array. Specify color saturation of each level, like [0.2, 0.5]
|
||
colorMappingBy: 'index', // 'value' or 'index' or 'id'.
|
||
visibleMin: 10, // If area less than this threshold (unit: pixel^2), node will not
|
||
// be rendered. Only works when sort is 'asc' or 'desc'.
|
||
childrenVisibleMin: null, // If area of a node less than this threshold (unit: pixel^2),
|
||
// grandchildren will not show.
|
||
// Why grandchildren? If not grandchildren but children,
|
||
// some siblings show children and some not,
|
||
// the appearance may be mess and not consistent,
|
||
levels: [] // Each item: {
|
||
// visibleMin, itemStyle, visualDimension, label
|
||
// }
|
||
// data: {
|
||
// value: [],
|
||
// children: [],
|
||
// link: 'http://xxx.xxx.xxx',
|
||
// target: 'blank' or 'self'
|
||
// }
|
||
},
|
||
|
||
/**
|
||
* @override
|
||
*/
|
||
getInitialData: function (option, ecModel) {
|
||
var data = option.data || [];
|
||
var rootName = option.name;
|
||
rootName == null && (rootName = option.name);
|
||
|
||
// Create a virtual root.
|
||
var root = {name: rootName, children: option.data};
|
||
var value0 = (data[0] || {}).value;
|
||
|
||
completeTreeValue(root, zrUtil.isArray(value0) ? value0.length : -1);
|
||
|
||
// FIXME
|
||
// sereis.mergeOption 的 getInitData是否放在merge后,从而能直接获取merege后的结果而非手动判断。
|
||
var levels = option.levels || [];
|
||
|
||
levels = option.levels = setDefault(levels, ecModel);
|
||
|
||
// Make sure always a new tree is created when setOption,
|
||
// in TreemapView, we check whether oldTree === newTree
|
||
// to choose mappings approach among old shapes and new shapes.
|
||
return Tree.createTree(root, this, levels).data;
|
||
},
|
||
|
||
optionUpdated: function () {
|
||
this.resetViewRoot();
|
||
},
|
||
|
||
/**
|
||
* @override
|
||
* @param {number} dataIndex
|
||
* @param {boolean} [mutipleSeries=false]
|
||
*/
|
||
formatTooltip: function (dataIndex) {
|
||
var data = this.getData();
|
||
var value = this.getRawValue(dataIndex);
|
||
var formattedValue = zrUtil.isArray(value)
|
||
? addCommas(value[0]) : addCommas(value);
|
||
var name = data.getName(dataIndex);
|
||
|
||
return encodeHTML(name) + ': ' + formattedValue;
|
||
},
|
||
|
||
/**
|
||
* Add tree path to tooltip param
|
||
*
|
||
* @override
|
||
* @param {number} dataIndex
|
||
* @return {Object}
|
||
*/
|
||
getDataParams: function (dataIndex) {
|
||
var params = SeriesModel.prototype.getDataParams.apply(this, arguments);
|
||
|
||
var data = this.getData();
|
||
var node = data.tree.getNodeByDataIndex(dataIndex);
|
||
var treePathInfo = params.treePathInfo = [];
|
||
|
||
while (node) {
|
||
var nodeDataIndex = node.dataIndex;
|
||
treePathInfo.push({
|
||
name: node.name,
|
||
dataIndex: nodeDataIndex,
|
||
value: this.getRawValue(nodeDataIndex)
|
||
});
|
||
node = node.parentNode;
|
||
}
|
||
|
||
treePathInfo.reverse();
|
||
|
||
return params;
|
||
},
|
||
|
||
/**
|
||
* @public
|
||
* @param {Object} layoutInfo {
|
||
* x: containerGroup x
|
||
* y: containerGroup y
|
||
* width: containerGroup width
|
||
* height: containerGroup height
|
||
* }
|
||
*/
|
||
setLayoutInfo: function (layoutInfo) {
|
||
/**
|
||
* @readOnly
|
||
* @type {Object}
|
||
*/
|
||
this.layoutInfo = this.layoutInfo || {};
|
||
zrUtil.extend(this.layoutInfo, layoutInfo);
|
||
},
|
||
|
||
/**
|
||
* @param {string} id
|
||
* @return {number} index
|
||
*/
|
||
mapIdToIndex: function (id) {
|
||
// A feature is implemented:
|
||
// index is monotone increasing with the sequence of
|
||
// input id at the first time.
|
||
// This feature can make sure that each data item and its
|
||
// mapped color have the same index between data list and
|
||
// color list at the beginning, which is useful for user
|
||
// to adjust data-color mapping.
|
||
|
||
/**
|
||
* @private
|
||
* @type {Object}
|
||
*/
|
||
var idIndexMap = this._idIndexMap;
|
||
|
||
if (!idIndexMap) {
|
||
idIndexMap = this._idIndexMap = {};
|
||
/**
|
||
* @private
|
||
* @type {number}
|
||
*/
|
||
this._idIndexMapCount = 0;
|
||
}
|
||
|
||
var index = idIndexMap[id];
|
||
if (index == null) {
|
||
idIndexMap[id] = index = this._idIndexMapCount++;
|
||
}
|
||
|
||
return index;
|
||
},
|
||
|
||
getViewRoot: function () {
|
||
return this._viewRoot;
|
||
},
|
||
|
||
/**
|
||
* @param {module:echarts/data/Tree~Node} [viewRoot]
|
||
*/
|
||
resetViewRoot: function (viewRoot) {
|
||
viewRoot
|
||
? (this._viewRoot = viewRoot)
|
||
: (viewRoot = this._viewRoot);
|
||
|
||
var root = this.getData().tree.root;
|
||
|
||
if (!viewRoot
|
||
|| (viewRoot !== root && !root.contains(viewRoot))
|
||
) {
|
||
this._viewRoot = root;
|
||
}
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @param {Object} dataNode
|
||
*/
|
||
function completeTreeValue(dataNode, arrValueLength) {
|
||
// Postorder travel tree.
|
||
// If value of none-leaf node is not set,
|
||
// calculate it by suming up the value of all children.
|
||
var sum = 0;
|
||
|
||
zrUtil.each(dataNode.children, function (child) {
|
||
|
||
completeTreeValue(child, arrValueLength);
|
||
|
||
var childValue = child.value;
|
||
zrUtil.isArray(childValue) && (childValue = childValue[0]);
|
||
|
||
sum += childValue;
|
||
});
|
||
|
||
var thisValue = dataNode.value;
|
||
|
||
if (arrValueLength >= 0) {
|
||
if (!zrUtil.isArray(thisValue)) {
|
||
dataNode.value = new Array(arrValueLength);
|
||
}
|
||
else {
|
||
thisValue = thisValue[0];
|
||
}
|
||
}
|
||
|
||
if (thisValue == null || isNaN(thisValue)) {
|
||
thisValue = sum;
|
||
}
|
||
// Value should not less than 0.
|
||
if (thisValue < 0) {
|
||
thisValue = 0;
|
||
}
|
||
|
||
arrValueLength >= 0
|
||
? (dataNode.value[0] = thisValue)
|
||
: (dataNode.value = thisValue);
|
||
}
|
||
|
||
/**
|
||
* set default to level configuration
|
||
*/
|
||
function setDefault(levels, ecModel) {
|
||
var globalColorList = ecModel.get('color');
|
||
|
||
if (!globalColorList) {
|
||
return;
|
||
}
|
||
|
||
levels = levels || [];
|
||
var hasColorDefine;
|
||
zrUtil.each(levels, function (levelDefine) {
|
||
var model = new Model(levelDefine);
|
||
var modelColor = model.get('color');
|
||
if (model.get('itemStyle.normal.color')
|
||
|| (modelColor && modelColor !== 'none')
|
||
) {
|
||
hasColorDefine = true;
|
||
}
|
||
});
|
||
|
||
if (!hasColorDefine) {
|
||
var level0 = levels[0] || (levels[0] = {});
|
||
level0.color = globalColorList.slice();
|
||
}
|
||
|
||
return levels;
|
||
}
|
||
|
||
}); |