提交 55bf1eac 编写于 作者: P pah100

Fix abnormal behaviour when use leaf depth.

上级 ac16326b
......@@ -17,6 +17,12 @@
var DRAG_THRESHOLD = 3;
var PATH_LABEL_NORMAL = ['label', 'normal'];
var PATH_LABEL_EMPHASIS = ['label', 'emphasis'];
var Z_BASE = 10; // Should bigger than every z.
var Z_BG = 0;
var Z_CONTENT = 4;
var Z_REROOT_DILLDOWN_ROOT_CONTENT = 3;
var Z_REROOT_DILLDOWN_OTHERS_CONTENT = 2;
var Z_REROOT_DILLDOWN_OTHERS = 1;
return require('../../echarts').extendChartView({
......@@ -103,7 +109,6 @@
var containerGroup = this._giveContainerGroup(layoutInfo);
var renderResult = this._doRender(containerGroup, seriesModel, reRoot);
(
!isInit && (
!payloadType
......@@ -148,15 +153,11 @@
var thisStorage = createStorage();
var oldStorage = this._storage;
var willInvisibleEls = [];
var willVisibleEls = [];
var willDeleteEls = [];
var doRenderNode = zrUtil.curry(
renderNode, this.seriesModel,
renderNode, seriesModel,
thisStorage, oldStorage, reRoot,
lastsForAnimation, willInvisibleEls, willVisibleEls
lastsForAnimation, willInvisibleEls
);
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
......@@ -183,7 +184,7 @@
renderFinally: renderFinally
};
function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, viewPathIndex) {
function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) {
// When 'render' is triggered by action,
// 'this' and 'old' may be the same tree,
// we use rawIndex in that case.
......@@ -212,25 +213,14 @@
var thisNode = newIndex != null ? thisViewChildren[newIndex] : null;
var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null;
// Whether under viewRoot.
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 = doRenderNode(thisNode, oldNode, parentGroup);
var group = doRenderNode(thisNode, oldNode, parentGroup, depth);
group && dualTravel(
thisNode && thisNode.viewChildren || [],
oldNode && oldNode.viewChildren || [],
group,
sameTree,
viewPathIndex + 1
depth + 1
);
}
}
......@@ -240,7 +230,7 @@
storage && each(storage, function (store, storageName) {
var delEls = willDeleteEls[storageName];
each(store, function (el) {
el && (delEls.push(el), el.__tmWillDelete = storageName);
el && (delEls.push(el), el.__tmWillDelete = 1);
});
});
return willDeleteEls;
......@@ -252,19 +242,12 @@
el.parent && el.parent.remove(el);
});
});
// Theoritically there is no intersection between willInvisibleEls
// and willVisibleEls have, but we set visible after for robustness.
each(willInvisibleEls, function (el) {
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;
el.__tmWillVisible = false;
el.dirty();
});
}
},
......@@ -283,9 +266,7 @@
// Make delete animations.
each(renderResult.willDeleteEls, function (store, storageName) {
each(store, function (el, rawIndex) {
var storageName;
if (el.invisible || !(storageName = el.__tmWillDelete)) {
if (el.invisible) {
return;
}
......@@ -294,18 +275,26 @@
if (reRoot && reRoot.direction === 'drilldown') {
if (parent === reRoot.rootNodeGroup) {
// Only 'content' will enter this branch, but not nodeGroup.
// Only 'content' will enter this branch, because
// background and nodeGroup will not be deleted.
target = {
shape: {
x: 0, y: 0,
width: parent.__tmNodeWidth, height: parent.__tmNodeHeight
x: 0,
y: 0,
width: parent.__tmNodeWidth,
height: parent.__tmNodeHeight
}
};
el.z = 2;
el.z = calculateZ(el.__tmDepth, Z_REROOT_DILLDOWN_ROOT_CONTENT);
}
else {
target = {style: {opacity: 0}};
el.z = 1;
el.z = calculateZ(
el.__tmDepth,
el.__tmStorageName === 'content'
? Z_REROOT_DILLDOWN_OTHERS_CONTENT
: Z_REROOT_DILLDOWN_OTHERS
);
}
}
else {
......@@ -319,6 +308,7 @@
targetX = parent.__tmNodeWidth / 2;
targetY = parent.__tmNodeHeight / 2;
}
target = storageName === 'nodeGroup'
? {position: [targetX, targetY], style: {opacity: 0}}
: {
......@@ -363,6 +353,7 @@
target.style = {opacity: 1};
}
}
animationWrap.add(el, target, duration, easing);
});
}, this);
......@@ -656,32 +647,53 @@
/**
* @inner
* @return Return undefined means do not travel further.
*/
function renderNode(
seriesModel, thisStorage, oldStorage, reRoot,
lastsForAnimation, willInvisibleEls, willVisibleEls,
thisNode, oldNode, parentGroup
lastsForAnimation, willInvisibleEls,
thisNode, oldNode, parentGroup, depth
) {
var thisRawIndex = thisNode && thisNode.getRawIndex();
var oldRawIndex = oldNode && oldNode.getRawIndex();
// Whether under viewRoot.
if (!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 layout = thisNode.getLayout();
if (!layout || !layout.isInView) {
return;
}
var thisWidth = layout.width;
var thisHeight = layout.height;
var invisible = layout.invisible;
var thisRawIndex = thisNode.getRawIndex();
var oldRawIndex = oldNode && oldNode.getRawIndex();
// Node group
var group = giveGraphic('nodeGroup', Group);
if (!group) {
return;
}
parentGroup.add(group);
group.position = [layout.x, layout.y];
// x,y are not set when el is above view root.
group.position = [layout.x || 0, layout.y || 0];
group.__tmNodeWidth = thisWidth;
group.__tmNodeHeight = thisHeight;
if (layout.isAboveViewRoot) {
return group;
}
// Background
var bg = giveGraphic('background', Rect, 0);
var bg = giveGraphic('background', Rect, depth, Z_BG);
if (bg) {
bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight});
updateStyle(bg, function () {
......@@ -694,7 +706,7 @@
// No children, render content.
if (!thisViewChildren || !thisViewChildren.length) {
var content = giveGraphic('content', Rect, 3);
var content = giveGraphic('content', Rect, depth, Z_CONTENT);
content && renderContent(layout, group, thisNode, thisWidth, thisHeight);
}
......@@ -792,7 +804,7 @@
}
}
function giveGraphic(storageName, Ctor, z) {
function giveGraphic(storageName, Ctor, depth, z) {
var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex];
var lasts = lastsForAnimation[storageName];
......@@ -803,7 +815,9 @@
}
// If invisible and no old element, do not create new element (for optimizing).
else if (!invisible) {
element = new Ctor({z: z});
element = new Ctor({z: calculateZ(depth, z)});
element.__tmDepth = depth;
element.__tmStorageName = storageName;
prepareAnimationWhenNoOld(lasts, element, storageName);
}
......@@ -821,37 +835,40 @@
// If a element is new, we need to find the animation start point carefully,
// otherwise it will looks strange when 'zoomToNode'.
function prepareAnimationWhenNoOld(lasts, element, storageName) {
// New background do not animate but delay show.
if (storageName === 'background') {
element.invisible = true;
element.__tmWillVisible = true;
willVisibleEls.push(element);
}
else {
var lastCfg = lasts[thisRawIndex] = {};
var parentNode = thisNode.parentNode;
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};
}
var lastCfg = lasts[thisRawIndex] = {};
var parentNode = thisNode.parentNode;
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()];
// Fade in, user can be aware that these nodes are new.
lastCfg.fadein = storageName !== 'nodeGroup';
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};
}
// Fade in, user can be aware that these nodes are new.
lastCfg.fadein = storageName !== 'nodeGroup';
}
}
// We can not set all backgroud with the same z, Because the behaviour of
// drilldown and rollup differ background creation sequence from tree
// hierarchy sequence, which cause that lowser background element overlap
// upper ones. So we calculate z based on depth.
// Moreover, we try to shrink down z interval to [0, 1] to avoid that
// treemap with large z overlaps other components.
function calculateZ(depth, zInLevel) {
var zb = depth * Z_BASE + zInLevel;
return (zb - 1) / zb;
}
});
\ No newline at end of file
......@@ -24,27 +24,20 @@ define(function (require) {
}
},
// Not includes the given node at the last item.
getPathToRoot: function (node) {
var path = [];
while (node) {
path.push(node);
node = node.parentNode;
node && path.push(node);
}
return path.reverse();
},
aboveViewRoot: function (viewRoot, node) {
var viewPath = helper.getPathToRoot(viewRoot);
return helper.aboveViewRootByViewPath(viewPath, node);
},
// 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;
return zrUtil.indexOf(viewPath, node) >= 0;
}
};
return helper;
......
......@@ -48,6 +48,7 @@ define(function (require) {
var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove')
? payload.rootRect : null;
var viewRoot = seriesModel.getViewRoot();
var viewAbovePath = helper.getPathToRoot(viewRoot);
if (payloadType !== 'treemapMove') {
var rootSize = payloadType === 'treemapZoomToNode'
......@@ -68,6 +69,14 @@ define(function (require) {
leafDepth: seriesOption.leafDepth
};
// layout should be cleared because using updateView but not update.
viewRoot.hostTree.clearLayouts();
// TODO
// optimize: if out of view clip, do not layout.
// But take care that if do not render node out of view clip,
// how to calculate start po
viewRoot.setLayout({
x: 0, y: 0,
width: rootSize[0], height: rootSize[1],
......@@ -75,6 +84,12 @@ define(function (require) {
});
squarify(viewRoot, options, false, 0);
// Supplement layout (dataExtent).
zrUtil.each(viewAbovePath, function (node, index) {
var childValue = (viewAbovePath[index + 1] || viewRoot).getValue();
node.setLayout({dataExtent: [childValue, childValue]});
});
}
// Set root position
......@@ -92,7 +107,9 @@ define(function (require) {
seriesModel.getData().tree.root,
// Transform to base element coordinate system.
new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight),
helper.getPathToRoot(viewRoot)
viewAbovePath,
viewRoot,
0
);
});
......@@ -477,8 +494,10 @@ define(function (require) {
var node = targetNode;
while (node) {
var nodeLayout = node.getLayout();
targetCenter[0] += nodeLayout.x;
targetCenter[1] += nodeLayout.y;
if (nodeLayout) {
targetCenter[0] += nodeLayout.x;
targetCenter[1] += nodeLayout.y;
}
node = node.parentNode;
}
......@@ -488,27 +507,41 @@ 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, viewPath) {
// Mark nodes visible for prunning when visual coding and rendering.
// Prunning depends on layout and root position, so we have to do it after layout.
function prunning(node, clipRect, viewAbovePath, viewRoot, depth) {
var nodeLayout = node.getLayout();
var nodeInViewAbovePath = viewAbovePath[depth];
var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node;
if (
(nodeInViewAbovePath && !isAboveViewRoot)
|| (depth === viewAbovePath.length && node !== viewRoot)
) {
return;
}
node.setLayout({
invisible: nodeLayout
? !clipRect.intersect(nodeLayout)
: !helper.aboveViewRootByViewPath(viewPath, node)
// isInView means: viewRoot sub tree + viewAbovePath
isInView: true,
// invisible only means: outside view clip so that the node can not
// see but still layout for animation preparation but not render.
invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout),
isAboveViewRoot: isAboveViewRoot
}, true);
var viewChildren = node.viewChildren || [];
for (var i = 0, len = viewChildren.length; i < len; i++) {
// Transform to child coordinate.
var childClipRect = new BoundingRect(
clipRect.x - nodeLayout.x,
clipRect.y - nodeLayout.y,
clipRect.width,
clipRect.height
);
prunning(viewChildren[i], childClipRect, viewPath);
var childClipRect = isAboveViewRoot
? clipRect
: new BoundingRect(
clipRect.x - nodeLayout.x,
clipRect.y - nodeLayout.y,
clipRect.width,
clipRect.height
);
prunning(viewChildren[i], childClipRect, viewAbovePath, viewRoot, depth + 1);
}
}
......
......@@ -25,7 +25,7 @@ define(function (require) {
});
travelTree(
root,
root, // Visual should calculate from tree root but not view root.
{},
levelItemStyles,
seriesItemStyleModel,
......@@ -43,7 +43,7 @@ define(function (require) {
var nodeLayout = node.getLayout();
// Optimize
if (nodeLayout.invisible) {
if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) {
return;
}
......
......@@ -911,7 +911,7 @@ define(function (require) {
*/
listProto.getItemLayout = function (idx) {
return this._itemLayouts[idx];
},
};
/**
* Set layout of single data item
......@@ -923,7 +923,14 @@ define(function (require) {
this._itemLayouts[idx] = merge
? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
: layout;
},
};
/**
* Clear all layout of single data item
*/
listProto.clearItemLayouts = function () {
this._itemLayouts.length = 0;
};
/**
* Get visual property of single data item
......@@ -939,7 +946,7 @@ define(function (require) {
return this.getVisual(key);
}
return val;
},
};
/**
* Set visual property of single data item
......
......@@ -254,7 +254,7 @@ define(function(require) {
},
/**
* @public
* Get item visual
*/
getVisual: function (key, ignoreParent) {
return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent);
......@@ -379,6 +379,13 @@ define(function(require) {
for (var i = 0, len = data.count(); i < len; i++) {
nodes[data.getRawIndex(i)].dataIndex = i;
}
},
/**
* Clear all layouts
*/
clearLayouts: function () {
this.data.clearItemLayouts();
}
};
......
......@@ -89,7 +89,8 @@ define(function (require) {
// which need no more preprocess except normalize visual.
: normalizeVisualRange(thisOption, true);
}
else {
else { // mappingMethod === 'linear'
zrUtil.assert(thisOption.dataExtent);
normalizeVisualRange(thisOption);
}
};
......
......@@ -77,18 +77,68 @@
name: 'option',
type: 'treemap',
visibleMin: 300,
// animationDurationUpdate: 2000,
data: data.children,
// data: [
// {
// name: 'a',
// value: 4,
// children: [
// {
// name: 'a1',
// value: 11,
// chidren: [
// {
// name: 'a11',
// value: 111
// }
// ]
// }
// ]
// },
// {
// name: 'b',
// value: 6,
// children: [
// {
// name: 'b1',
// value: 15,
// chidren: [
// {
// name: 'b11',
// value: 120
// }
// ]
// }
// ]
// }
// ],
leafDepth: 1,
nodeClick: 'link',
itemStyle: {
normal: {
// gapWidth: 1
// borderWidth: 1
}
// normal: {
// gapWidth: 1,
// borderWidth: 1
// }
},
levels: [
{},
{
itemStyle: {
normal: {
borderColor: '#333',
borderWidth: 4,
gapWidth: 2
}
}
},
{
itemStyle: {
normal: {
borderColor: '#aaa',
gapWidth: 2,
borderWidth: 2
}
},
colorSaturation: [0.2, 0.7]
}
]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册