提交 abdb25cf 编写于 作者: P pah100

treemap drill down (part I)

上级 a63ac125
......@@ -127,6 +127,7 @@
textFont: textStyleModel.getFont()
}
),
z: 10,
onclick: zrUtil.bind(this._onSelect, this, item.node)
}));
......
......@@ -5,6 +5,7 @@ define(function(require) {
var zrUtil = require('zrender/core/util');
var Model = require('../../model/Model');
var formatUtil = require('../../util/format');
var helper = require('./helper');
var encodeHTML = formatUtil.encodeHTML;
var addCommas = formatUtil.addCommas;
......@@ -15,9 +16,14 @@ define(function(require) {
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.
// center: ['50%', '50%'], // not supported in ec3.
// size: ['80%', '80%'], // deprecated, compatible with ec2.
left: 'center',
top: 'middle',
right: null,
......@@ -26,15 +32,20 @@ define(function(require) {
height: '80%',
sort: true, // Can be null or false or true
// (order by desc default, asc not supported yet (strange effect))
clipWindow: 'origin', // 缩放时窗口大小。'origin' or 'fullscreen'
clipWindow: 'origin', // Size of clipped window when zooming. 'origin' or 'fullscreen'
squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
root: null, // default: tree root. This feature doesnt work unless node have id.
leafDepth: null, // Nodes on depth from root are regarded as leaves.
// Count from zero (zero represents only view root).
visualDimension: 0, // Can be 0, 1, 2, 3.
zoomToNodeRatio: 0.32 * 0.32, // zoom to node时 node占可视区域的面积比例。
roam: true, // true, false, 'scale' or 'zoom', 'move'
nodeClick: 'zoomToNode', // 'zoomToNode', 'link', false
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: 1500,
animationDurationUpdate: 900,
animationEasing: 'quinticInOut',
breadcrumb: {
show: true,
......@@ -43,7 +54,7 @@ define(function(require) {
top: 'bottom',
// right
// bottom
emptyItemWidth: 25, // 空节点宽度
emptyItemWidth: 25, // Width of empty node.
itemStyle: {
normal: {
color: 'rgba(0,0,0,0.7)', //'#5793f3',
......@@ -65,7 +76,7 @@ define(function(require) {
label: {
normal: {
show: true,
position: ['50%', '50%'], // 可以是 5 '5%' 'insideTopLeft', ...
position: ['50%', '50%'], // Can be 5, '5%' or position stirng like 'insideTopLeft', ...
textStyle: {
align: 'center',
baseline: 'middle',
......@@ -76,30 +87,35 @@ define(function(require) {
},
itemStyle: {
normal: {
color: null, // 各异 如不需,可设为'none'
colorAlpha: null, // 默认不设置 如不需,可设为'none'
colorSaturation: null, // 默认不设置 如不需,可设为'none'
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 // 如果设置,则borderColor的设置无效,而是取当前节点计算出的颜色,再经由borderColorSaturation处理。
borderColorSaturation: null // If specified, borderColor will be ineffective, and the
// border color is evaluated by color of current node and
// borderColorSaturation.
},
emphasis: {}
emphasis: {
}
},
color: 'none', // 为数组,表示同一level的color 选取列表。默认空,在level[0].color中取系统color列表。
colorAlpha: null, // 为数组,表示同一level的color alpha 选取范围。
colorSaturation: null, // 为数组,表示同一level的color alpha 选取范围。
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
// }
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: [],
......@@ -134,13 +150,8 @@ define(function(require) {
return Tree.createTree(root, this, levels).data;
},
/**
* @public
*/
getViewRoot: function () {
var optionRoot = this.option.root;
var treeRoot = this.getData().tree.root;
return optionRoot && treeRoot.getNodeById(optionRoot) || treeRoot;
optionUpdated: function () {
this.resetViewRoot();
},
/**
......@@ -239,6 +250,28 @@ define(function(require) {
}
return index;
},
getViewRoot: function () {
return this._viewRoot;
},
/**
* @param {module:echarts/data/Tree~Node} [viewRoot]
* @return {string} direction 'drilldown' or 'rollup'
*/
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;
}
}
});
......
......@@ -84,21 +84,36 @@
this.api = api;
this.ecModel = ecModel;
var targetInfo = helper.retrieveTargetInfo(payload, seriesModel);
var payloadType = payload && payload.type;
var layoutInfo = seriesModel.layoutInfo;
var isInit = !this._oldTree;
var thisStorage = this._storage;
// Mark new root when action is treemapRootToNode.
var reRoot = (payloadType === 'treemapRootToNode' && targetInfo && thisStorage)
? {
rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()],
direction: payload.direction
}
: null;
var containerGroup = this._giveContainerGroup(layoutInfo);
var renderResult = this._doRender(containerGroup, seriesModel);
var renderResult = this._doRender(containerGroup, seriesModel, reRoot);
(!isInit && (!payloadType || payloadType === 'treemapZoomToNode'))
? this._doAnimation(containerGroup, renderResult, seriesModel)
(
!isInit && (
!payloadType
|| payloadType === 'treemapZoomToNode'
|| payloadType === 'treemapRootToNode'
)
)
? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot)
: renderResult.renderFinally();
this._resetController(api);
var targetInfo = helper.retrieveTargetInfo(payload, seriesModel);
this._renderBreadcrumb(seriesModel, api, targetInfo);
},
......@@ -122,7 +137,7 @@
/**
* @private
*/
_doRender: function (containerGroup, seriesModel) {
_doRender: function (containerGroup, seriesModel, reRoot) {
var thisTree = seriesModel.getData().tree;
var oldTree = this._oldTree;
......@@ -135,9 +150,11 @@
var willDeleteEls = [];
var renderNode = bind(
this._renderNode, this,
thisStorage, oldStorage, lastsForAnimation, willInvisibleEls, willVisibleEls
thisStorage, oldStorage, reRoot,
lastsForAnimation, willInvisibleEls, willVisibleEls
);
var viewRoot = seriesModel.getViewRoot();
var viewPath = helper.getPathToRoot(viewRoot);
// Notice: when thisTree and oldTree are the same tree (see list.cloneShadow),
// the oldTree is actually losted, so we can not find all of the old graphic
......@@ -149,7 +166,7 @@
(oldTree && oldTree.root) ? [oldTree.root] : [],
containerGroup,
thisTree === oldTree || !oldTree,
viewRoot === thisTree.root
0
);
// Process all removing.
......@@ -164,7 +181,7 @@
renderFinally: renderFinally
};
function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, inView) {
function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, viewPathIndex) {
// When 'render' is triggered by action,
// 'this' and 'old' may be the same tree,
// we use rawIndex in that case.
......@@ -194,10 +211,14 @@
var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;
// Whether under viewRoot.
var subInView = inView || thisNode === viewRoot;
// If not under viewRoot, only remove.
if (!subInView) {
thisNode = null;
if (!thisNode
|| isNaN(viewPathIndex)
|| (viewPathIndex < viewPath.length && viewPath[viewPathIndex] !== thisNode)
) {
// Deleting nodes will be performed finally. This method just find
// element from old storage, or create new element, set them to new
// storage, and set styles.
return;
}
var group = renderNode(thisNode, oldNode, parentGroup);
......@@ -207,7 +228,7 @@
oldNode && oldNode.viewChildren || [],
group,
sameTree,
subInView
viewPathIndex + 1
);
}
}
......@@ -235,6 +256,7 @@
el.invisible = true;
// Setting invisible is for optimizing, so no need to set dirty,
// just mark as invisible.
el.dirty();
});
each(willVisibleEls, function (el) {
el.invisible = false;
......@@ -248,19 +270,13 @@
* @private
*/
_renderNode: function (
thisStorage, oldStorage, lastsForAnimation,
willInvisibleEls, willVisibleEls,
thisStorage, oldStorage, reRoot,
lastsForAnimation, willInvisibleEls, willVisibleEls,
thisNode, oldNode, parentGroup
) {
var thisRawIndex = thisNode && thisNode.getRawIndex();
var oldRawIndex = oldNode && oldNode.getRawIndex();
// Deleting things will performed finally. This method just find element from
// old storage, or create new element, set them to new storage, and set styles.
if (!thisNode) {
return;
}
var layout = thisNode.getLayout();
var thisWidth = layout.width;
var thisHeight = layout.height;
......@@ -277,7 +293,7 @@
group.__tmNodeHeight = thisHeight;
// Background
var bg = giveGraphic('background', Rect);
var bg = giveGraphic('background', Rect, 0);
if (bg) {
bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight});
updateStyle(bg, {fill: thisNode.getVisual('borderColor', true)});
......@@ -289,13 +305,13 @@
// No children, render content.
if (!thisViewChildren || !thisViewChildren.length) {
var borderWidth = layout.borderWidth;
var content = giveGraphic('content', Rect);
var content = giveGraphic('content', Rect, 3);
if (content) {
var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0);
var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0);
var labelModel = thisNode.getModel('label.normal');
var textStyleModel = thisNode.getModel('label.normal.textStyle');
var hoverStyle = thisNode.getModel('itemStyle.emphasis').getItemStyle();
var text = thisNode.getModel().get('name');
var textRect = textStyleModel.getTextRect(text);
var showLabel = labelModel.get('show');
......@@ -308,6 +324,8 @@
? textStyleModel.ellipsis(text, contentWidth) : '';
}
graphic.setHoverStyle(content, hoverStyle);
// For tooltip.
content.dataIndex = thisNode.dataIndex;
content.seriesIndex = this.seriesModel.seriesIndex;
......@@ -319,6 +337,7 @@
width: contentWidth,
height: contentHeight
});
updateStyle(content, {
fill: thisNode.getVisual('color', true),
text: text,
......@@ -334,7 +353,7 @@
return group;
function giveGraphic(storageName, Ctor) {
function giveGraphic(storageName, Ctor, z) {
var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex];
var lasts = lastsForAnimation[storageName];
......@@ -345,7 +364,7 @@
}
// If invisible and no old element, do not create new element (for optimizing).
else if (!invisible) {
element = new Ctor();
element = new Ctor({z: z});
prepareAnimationWhenNoOld(lasts, element, storageName);
}
......@@ -356,9 +375,9 @@
function prepareAnimationWhenHasOld(lasts, element, storageName) {
var lastCfg = lasts[thisRawIndex] = {};
lastCfg.old = storageName === 'nodeGroup'
? element.position.slice()
: zrUtil.extend({}, element.shape);
}
? element.position.slice()
: zrUtil.extend({}, element.shape);
}
// If a element is new, we need to find the animation start point carefully,
// otherwise it will looks strange when 'zoomToNode'.
......@@ -370,23 +389,25 @@
willVisibleEls.push(element);
}
else {
var lastCfg = lasts[thisRawIndex] = {};
var parentNode = thisNode.parentNode;
var parentOldBg;
var parentOldX = 0;
var parentOldY = 0;
// For convenient, get old bounding rect from background.
if (parentNode && (
parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()]
)) {
parentOldX = parentOldBg.old.width;
parentOldY = parentOldBg.old.height;
if (parentNode && (!reRoot || reRoot.direction === 'drilldown')) {
var parentOldX = 0;
var parentOldY = 0;
// For convenience, get old bounding rect from background.
var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()];
if (parentOldBg && parentOldBg.old) {
parentOldX = parentOldBg.old.width / 2; // Devided by 2 for reRoot effect.
parentOldY = parentOldBg.old.height / 2;
}
// When no parent old shape found, its parent is new too,
// so we can just use {x:0, y:0}.
lastCfg.old = storageName === 'nodeGroup'
? [parentOldX, parentOldY]
: {x: parentOldX, y: parentOldY, width: 0, height: 0};
}
// When no parent old shape found, its parent is new too,
// so we can just use {x:0, y:0}.
var lastCfg = lasts[thisRawIndex] = {};
lastCfg.old = storageName === 'nodeGroup'
? [parentOldX, parentOldY]
: {x: parentOldX, y: parentOldY, width: 0, height: 0};
// Fade in, user can be aware that these nodes are new.
lastCfg.fadein = storageName !== 'nodeGroup';
......@@ -414,56 +435,87 @@
/**
* @private
*/
_doAnimation: function (containerGroup, renderResult, seriesModel) {
_doAnimation: function (containerGroup, renderResult, seriesModel, reRoot) {
if (!seriesModel.get('animation')) {
return;
}
var duration = seriesModel.get('animationDurationUpdate');
var easing = seriesModel.get('animationEasing');
var animationWrap = animationUtil.createWrap();
// Make delete animations.
var viewRoot = this.seriesModel.getViewRoot();
var rootGroup = this._storage.nodeGroup[viewRoot.getRawIndex()];
rootGroup && rootGroup.traverse(function (el) {
var storageName;
if (el.invisible || !(storageName = el.__tmWillDelete)) {
return;
}
var targetX = 0;
var targetY = 0;
var parent = el.parent; // Always has parent, and parent is nodeGroup.
if (!parent.__tmWillDelete) {
// Let node animate to right-bottom corner, cooperating with fadeout,
// which is perfect for user understanding.
targetX = parent.__tmNodeWidth;
targetY = parent.__tmNodeHeight;
}
var target = storageName === 'nodeGroup'
? {position: [targetX, targetY], style: {opacity: 0}}
: {shape: {x: targetX, y: targetY, width: 0, height: 0}, style: {opacity: 0}};
animationWrap.add(el, target, duration, easing);
each(renderResult.willDeleteEls, function (store, storageName) {
each(store, function (el, rawIndex) {
var storageName;
if (el.invisible || !(storageName = el.__tmWillDelete)) {
return;
}
var parent = el.parent; // Always has parent, and parent is nodeGroup.
var target;
if (reRoot && reRoot.direction === 'drilldown') {
if (parent === reRoot.rootNodeGroup) {
// Only 'content' will enter this branch, but not nodeGroup.
target = {
shape: {
x: 0, y: 0,
width: parent.__tmNodeWidth, height: parent.__tmNodeHeight
}
};
el.z = 2;
}
else {
target = {style: {opacity: 0}};
el.z = 1;
}
}
else {
var targetX = 0;
var targetY = 0;
if (!parent.__tmWillDelete) {
// Let node animate to right-bottom corner, cooperating with fadeout,
// which is appropriate for user understanding.
// Divided by 2 for reRoot rollup effect.
targetX = parent.__tmNodeWidth / 2;
targetY = parent.__tmNodeHeight / 2;
}
target = storageName === 'nodeGroup'
? {position: [targetX, targetY], style: {opacity: 0}}
: {
shape: {x: targetX, y: targetY, width: 0, height: 0},
style: {opacity: 0}
};
}
target && animationWrap.add(el, target, duration, easing);
});
});
// Make other animations
each(this._storage, function (store, storageName) {
each(store, function (el, rawIndex) {
var last = renderResult.lastsForAnimation[storageName][rawIndex];
var target;
var target = {};
if (!last) {
return;
}
if (storageName === 'nodeGroup') {
target = {position: el.position.slice()};
el.position = last.old;
if (last.old) {
target.position = el.position.slice();
el.position = last.old;
}
}
else {
target = {shape: zrUtil.extend({}, el.shape)};
el.setShape(last.old);
if (last.old) {
target.shape = zrUtil.extend({}, el.shape);
el.setShape(last.old);
}
if (last.fadein) {
el.setStyle('opacity', 0);
......@@ -631,12 +683,19 @@
var targetInfo = this.findTarget(e.offsetX, e.offsetY);
if (targetInfo) {
if (!targetInfo) {
return;
}
var node = targetInfo.node;
if (node.getLayout().isLeafRoot) {
this._rootToNode(targetInfo);
}
else {
if (nodeClick === 'zoomToNode') {
this._zoomToNode(targetInfo);
}
else if (nodeClick === 'link') {
var node = targetInfo.node;
var itemModel = node.hostTree.data.getItemModel(node.dataIndex);
var link = itemModel.get('link', true);
var linkTarget = itemModel.get('target', true) || 'blank';
......@@ -663,7 +722,11 @@
.render(seriesModel, api, targetInfo.node);
function onSelect(node) {
this._zoomToNode({node: node});
if (this._state !== 'animating') {
helper.aboveViewRoot(seriesModel.getViewRoot(), node)
? this._rootToNode({node: node})
: this._zoomToNode({node: node});
}
}
},
......@@ -694,6 +757,18 @@
});
},
/**
* @private
*/
_rootToNode: function (targetInfo) {
this.api.dispatchAction({
type: 'treemapRootToNode',
from: this.uid,
seriesId: this.seriesModel.id,
targetNode: targetInfo.node
});
},
/**
* @public
* @param {number} x Global coord x.
......@@ -736,4 +811,5 @@
function createStorage() {
return {nodeGroup: [], background: [], content: []};
}
});
\ No newline at end of file
define(function (require) {
var zrUtil = require('zrender/core/util');
var helper = {
retrieveTargetInfo: function (payload, seriesModel) {
if (!payload || payload.type !== 'treemapZoomToNode') {
return;
}
if (payload
&& (
payload.type === 'treemapZoomToNode'
|| payload.type === 'treemapRootToNode'
)
) {
var root = seriesModel.getData().tree.root;
var targetNode = payload.targetNode;
if (targetNode && root.contains(targetNode)) {
return {node: targetNode};
}
var root = seriesModel.getData().tree.root;
var targetNode = payload.targetNode;
if (targetNode && root.contains(targetNode)) {
return {node: targetNode};
var targetNodeId = payload.targetNodeId;
if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {
return {node: targetNode};
}
}
},
var targetNodeId = payload.targetNodeId;
if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {
return {node: targetNode};
getPathToRoot: function (node) {
var path = [];
while (node) {
path.push(node);
node = node.parentNode;
}
return path.reverse();
},
aboveViewRoot: function (viewRoot, node) {
var viewPath = helper.getPathToRoot(viewRoot);
return helper.aboveViewRootByViewPath(viewPath, node);
},
return null;
// viewPath should obtained from getPathToRoot(viewRoot)
aboveViewRootByViewPath: function (viewPath, node) {
var index = zrUtil.indexOf(viewPath, node);
// The last one is viewRoot
return index >= 0 && index !== viewPath.length - 1;
}
};
......
......@@ -4,11 +4,39 @@
define(function(require) {
var echarts = require('../../echarts');
var helper = require('./helper');
var noop = function () {};
echarts.registerAction({type: 'treemapZoomToNode', update: 'updateView'}, noop);
echarts.registerAction({type: 'treemapRender', update: 'updateView'}, noop);
echarts.registerAction({type: 'treemapMove', update: 'updateView'}, noop);
var actionTypes = [
'treemapZoomToNode',
'treemapRender',
'treemapMove'
];
for (var i = 0; i < actionTypes.length; i++) {
echarts.registerAction({type: actionTypes[i], update: 'updateView'}, noop);
}
echarts.registerAction(
{type: 'treemapRootToNode', update: 'updateView'},
function (payload, ecModel) {
ecModel.eachComponent(
{mainType: 'series', subType: 'treemap', query: payload},
function (model, index) {
var targetInfo = helper.retrieveTargetInfo(payload, model);
if (targetInfo) {
var originViewRoot = model.getViewRoot();
if (originViewRoot) {
payload.direction = helper.aboveViewRoot(originViewRoot, targetInfo.node)
? 'rollup' : 'drilldown';
}
model.resetViewRoot(targetInfo.node);
}
}
);
}
);
});
\ No newline at end of file
......@@ -5,6 +5,7 @@ define(function (require) {
var zrUtil = require('zrender/core/util');
var numberUtil = require('../../util/number');
var layout = require('../../util/layout');
var helper = require('./helper');
var parsePercent = numberUtil.parsePercent;
var retrieveValue = zrUtil.retrieve;
var BoundingRect = require('zrender/core/BoundingRect');
......@@ -21,14 +22,15 @@ define(function (require) {
var ecWidth = api.getWidth();
var ecHeight = api.getHeight();
var seriesOption = seriesModel.option;
var size = seriesModel.get('size') || []; // Compatible with ec2.
var size = seriesOption.size || []; // Compatible with ec2.
var containerWidth = parsePercent(
retrieveValue(seriesModel.get('width'), size[0]),
retrieveValue(seriesOption.width, size[0]),
ecWidth
);
var containerHeight = parsePercent(
retrieveValue(seriesModel.get('height'), size[1]),
retrieveValue(seriesOption.height, size[1]),
ecHeight
);
......@@ -49,18 +51,21 @@ define(function (require) {
if (payloadType !== 'treemapMove') {
var rootSize = payloadType === 'treemapZoomToNode'
? estimateRootSize(seriesModel, targetInfo, containerWidth, containerHeight)
? estimateRootSize(
seriesModel, targetInfo, viewRoot, containerWidth, containerHeight
)
: rootRect
? [rootRect.width, rootRect.height]
: [containerWidth, containerHeight];
var sort = seriesModel.get('sort');
var sort = seriesOption.sort;
if (sort && sort !== 'asc' && sort !== 'desc') {
sort = 'desc';
}
var options = {
squareRatio: seriesModel.get('squareRatio'),
sort: sort
squareRatio: seriesOption.squareRatio,
sort: sort,
leafDepth: seriesOption.leafDepth
};
viewRoot.setLayout({
......@@ -69,7 +74,7 @@ define(function (require) {
area: rootSize[0] * rootSize[1]
});
squarify(viewRoot, options);
squarify(viewRoot, options, false, 0);
}
// Set root position
......@@ -84,8 +89,10 @@ define(function (require) {
// FIXME
// 现在没有clip功能,暂时取ec高宽。
prunning(
viewRoot,
new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight)
seriesModel.getData().tree.root,
// Transform to base element coordinate system.
new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight),
helper.getPathToRoot(viewRoot)
);
});
......@@ -100,10 +107,11 @@ define(function (require) {
* @param {module:echarts/data/Tree~TreeNode} node
* @param {Object} options
* @param {string} options.sort 'asc' or 'desc'
* @param {boolean} options.hideChildren
* @param {number} options.squareRatio
* @param {boolean} hideChildren
* @param {number} depth
*/
function squarify(node, options) {
function squarify(node, options, hideChildren, depth) {
var width;
var height;
......@@ -128,7 +136,9 @@ define(function (require) {
height = mathMax(height - 2 * layoutOffset, 0);
var totalArea = width * height;
var viewChildren = initChildren(node, nodeModel, totalArea, options);
var viewChildren = initChildren(
node, nodeModel, totalArea, options, hideChildren, depth
);
if (!viewChildren.length) {
return;
......@@ -166,9 +176,7 @@ define(function (require) {
position(row, rowFixedLength, rect, halfGapWidth, true);
}
// Update option carefully.
var hideChildren;
if (!options.hideChildren) {
if (!hideChildren) {
var childrenVisibleMin = nodeModel.get('childrenVisibleMin');
if (childrenVisibleMin != null && totalArea < childrenVisibleMin) {
hideChildren = true;
......@@ -176,23 +184,22 @@ define(function (require) {
}
for (var i = 0, len = viewChildren.length; i < len; i++) {
var childOption = zrUtil.extend({
hideChildren: hideChildren
}, options);
squarify(viewChildren[i], childOption);
squarify(viewChildren[i], options, hideChildren, depth + 1);
}
}
/**
* Set area to each child, and calculate data extent for visual coding.
*/
function initChildren(node, nodeModel, totalArea, options) {
function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) {
var viewChildren = node.children || [];
var orderBy = options.sort;
orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null);
if (options.hideChildren) {
var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth;
// leafDepth has higher priority.
if (hideChildren && !overLeafDepth) {
return (node.viewChildren = []);
}
......@@ -222,6 +229,11 @@ define(function (require) {
viewChildren[i].setLayout({area: area});
}
if (overLeafDepth) {
viewChildren.length && node.setLayout({isLeafRoot: true}, true);
viewChildren.length = 0;
}
node.viewChildren = viewChildren;
node.setLayout({dataExtent: info.dataExtent}, true);
......@@ -393,19 +405,19 @@ define(function (require) {
}
// Return [containerWidth, containerHeight] as defualt.
function estimateRootSize(seriesModel, targetInfo, containerWidth, containerHeight) {
function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) {
// If targetInfo.node exists, we zoom to the node,
// so estimate whold width and heigth by target node.
var currNode = (targetInfo || {}).node;
var defaultSize = [containerWidth, containerHeight];
if (!currNode || currNode === seriesModel.getViewRoot()) {
if (!currNode || currNode === viewRoot) {
return defaultSize;
}
var parent;
var viewArea = containerWidth * containerHeight;
var area = viewArea * seriesModel.get('zoomToNodeRatio');
var area = viewArea * seriesModel.option.zoomToNodeRatio;
while (parent = currNode.parentNode) { // jshint ignore:line
var sum = 0;
......@@ -478,10 +490,14 @@ define(function (require) {
// Mark invisible nodes for prunning when visual coding and rendering.
// Prunning depends on layout and root position, so we have to do it after them.
function prunning(node, clipRect) {
function prunning(node, clipRect, viewPath) {
var nodeLayout = node.getLayout();
node.setLayout({invisible: !clipRect.intersect(nodeLayout)}, true);
node.setLayout({
invisible: nodeLayout
? !clipRect.intersect(nodeLayout)
: !helper.aboveViewRootByViewPath(viewPath, node)
}, true);
var viewChildren = node.viewChildren || [];
for (var i = 0, len = viewChildren.length; i < len; i++) {
......@@ -492,7 +508,7 @@ define(function (require) {
clipRect.width,
clipRect.height
);
prunning(viewChildren[i], childClipRect);
prunning(viewChildren[i], childClipRect, viewPath);
}
}
......
......@@ -55,6 +55,15 @@
<input type="radio" id="colorMapping-1" name="colorMapping" onclick="colorMappingChange('value');"/>
<label for="colorMapping-1">byValue</label>
</span>
<span class="item">
<span class="item-title">leafDepth:</span>
<input type="radio" id="leafDepth-0" name="leafDepth" onclick="leafDepthChange(null);" checked="checked"/>
<label for="leafDepth-0">Not set</label>
<input type="radio" id="leafDepth-1" name="leafDepth" onclick="leafDepthChange(1);"/>
<label for="leafDepth-1">Set to 1</label>
<input type="radio" id="leafDepth-2" name="leafDepth" onclick="leafDepthChange(2);"/>
<label for="leafDepth-1">Set to 2</label>
</span>
</div>
<div id="main"></div>
......@@ -94,6 +103,14 @@
});
}
function leafDepthChange(value) {
chart.setOption({
series: [{
leafDepth: value
}]
});
}
function getLevelOption(colorMapping) {
return [
{
......
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Option View</title>
<script src="esl.js"></script>
<script src="config.js"></script>
<style type="text/css">
body {
margin: 0;
}
html, body, #option-view-chart {
height: 100%;
}
</style>
</head>
<body>
<div id="option-view-chart"></div>
<script src="./lib/jquery.min.js"></script>
<script>
var echarts;
var formatUtil;
var chart;
require([
'echarts',
'echarts/util/format',
'echarts/component/tooltip',
'echarts/component/legend',
'echarts/chart/treemap'
], function (ec, format) {
echarts = ec;
formatUtil = format;
chart = echarts.init($('#option-view-chart')[0]);
chart.showLoading();
$.getJSON('./data/option-view.json', initEcharts);
});
function convert(source, target, basePath) {
for (var key in source) {
var path = basePath ? (basePath + '.' + key) : key;
if (key.match(/^\$/)) {
}
else {
target.children = target.children || [];
var child = {
name: path
};
target.children.push(child);
convert(source[key], child, path);
}
}
target.value = source.$count || 1;
}
function initEcharts(rawData) {
chart.hideLoading();
var data = {};
convert(rawData, data, '');
chart.setOption({
title: {
text: '配置项查询分布',
left: 'center'
},
tooltip: {},
series: [{
name: 'option',
type: 'treemap',
visibleMin: 300,
data: data.children,
leafDepth: 1,
nodeClick: 'link',
itemStyle: {
normal: {
// gapWidth: 1
// borderWidth: 1
}
},
levels: [
{},
{
colorSaturation: [0.2, 0.7]
}
]
}]
});
}
$(window).resize(function () {
chart && chart.resize();
})
</script>
</body>
</html>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册