提交 cf256218 编写于 作者: P pah100

treemap: fix bug and adjust color mapping

上级 f136a76e
......@@ -61,7 +61,7 @@ define(function (require) {
// Fetch payload info.
var payloadType = payload && payload.type;
var targetInfo = helper.retrieveTargetNodeInfo(payload, seriesModel);
var targetInfo = helper.retrieveTargetInfo(payload, seriesModel);
var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove')
? payload.rootRect : null;
var viewRoot = seriesModel.getViewRoot();
......@@ -468,7 +468,7 @@ define(function (require) {
return defaultPosition;
}
// If targetInfo is fetched by 'retrieveTargetNodeInfo',
// If targetInfo is fetched by 'retrieveTargetInfo',
// old tree and new tree are the same tree,
// so the node still exists and we can visit it.
......
......@@ -30,7 +30,7 @@ define(function(require) {
// (order by desc default, asc not supported yet (strange effect))
clipWindow: 'origin', // 缩放时窗口大小。'origin' or 'fullscreen'
squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio
root: '',
root: null, // default: tree root. This feature doesnt work unless node have id.
visualDimension: 'value', // 默认第一个维度。
zoomToNodeRatio: 0.32 * 0.32, // zoom to node时 node占可视区域的面积比例。
roam: true,
......@@ -70,11 +70,13 @@ define(function(require) {
position: ['50%', '50%'], // 可以是 5 '5%' 'insideTopLeft', ...
textStyle: {
align: 'center',
baseline: 'middle',
color: '#fff',
fontFamily: 'Arial',
fontSize: 13,
fontStyle: 'normal',
fontWeight: 'normal'
fontWeight: 'normal',
ellipsis: false
}
}
},
......@@ -93,7 +95,7 @@ define(function(require) {
color: 'none', // 为数组,表示同一level的color 选取列表。默认空,在level[0].color中取系统color列表。
colorA: null, // 为数组,表示同一level的color alpha 选取范围。
colorS: null, // 为数组,表示同一level的color alpha 选取范围。
colorMapping: 'byIndex', // 'byIndex' or 'byValue'
colorMapping: 'byIndex', // 'byIndex' or 'byValue' or 'byId'
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),
......@@ -138,7 +140,7 @@ define(function(require) {
getViewRoot: function () {
var optionRoot = this.option.root;
var treeRoot = this.getData().tree.root;
return optionRoot && treeRoot.getNodeByName(optionRoot) || treeRoot;
return optionRoot && treeRoot.getNodeById(optionRoot) || treeRoot;
},
/**
......@@ -201,6 +203,42 @@ define(function(require) {
*/
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;
}
});
......
......@@ -95,7 +95,7 @@
this._resetController(api);
var targetInfo = helper.retrieveTargetNodeInfo(payload, seriesModel);
var targetInfo = helper.retrieveTargetInfo(payload, seriesModel);
this._renderBreadcrumb(seriesModel, api, targetInfo);
},
......@@ -183,7 +183,7 @@
function getKey(node) {
// Identify by name or raw index.
return node.name != null ? node.name : node.getRawIndex();
return node.getId();
}
function processNode(newIndex, oldIndex) {
......@@ -296,14 +296,13 @@
var textRect = textStyleModel.getTextRect(text);
var showLabel = labelModel.get('show');
if (!showLabel
|| (
showLabel !== 'always'
&& (textRect.width > contentWidth || textRect.height > contentHeight)
)
) {
if (!showLabel || textRect.height > contentHeight) {
text = '';
}
else if (textRect.width > contentWidth) {
text = textStyleModel.get('ellipsis')
? textStyleModel.ellipsis(text, contentWidth) : '';
}
// For tooltip.
content.dataIndex = thisNode.dataIndex;
......@@ -322,6 +321,7 @@
textPosition: this._getTextPosition(labelModel, thisWidth, thisHeight),
textFill: textStyleModel.get('color'),
textAlign: textStyleModel.get('align'),
textBaseline: textStyleModel.get('baseline'),
textFont: textStyleModel.getFont()
});
group.add(content);
......@@ -481,6 +481,11 @@
el.setStyle('opacity', 0);
target.style = {opacity: 1};
}
// When animation is stopped for succedent animation starting,
// el.style.opacity might not be 1
else if (el.style.opacity !== 1) {
target.style = {opacity: 1};
}
}
animationWrap.add(el, target, duration, easing);
});
......@@ -539,10 +544,14 @@
var rootLayout = viewRoot.getLayout();
if (!rootLayout) {
return;
}
this.api.dispatch({
type: 'treemapMove',
from: this.uid,
seriesId: this.seriesModel.uid,
seriesUID: this.seriesModel.uid,
rootRect: {
x: rootLayout.x + dx, y: rootLayout.y + dy,
width: rootLayout.width, height: rootLayout.height
......@@ -564,6 +573,11 @@
}
var rootLayout = viewRoot.getLayout();
if (!rootLayout) {
return;
}
var rect = new BoundingRect(
rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height
);
......@@ -584,7 +598,7 @@
this.api.dispatch({
type: 'treemapRender',
from: this.uid,
seriesId: this.seriesModel.uid,
seriesUID: this.seriesModel.uid,
rootRect: {
x: rect.x, y: rect.y,
width: rect.width, height: rect.height
......@@ -661,8 +675,8 @@
this.api.dispatch({
type: 'treemapZoomToNode',
from: this.uid,
seriesId: this.seriesModel.uid,
targetInfo: targetInfo
seriesUID: this.seriesModel.uid,
targetNode: targetInfo.node
});
},
......
define(function (require) {
return {
var helper = {
irrelevant: function (payload, seriesModel) {
return payload && payload.seriesId && seriesModel.uid !== payload.seriesId;
// It is irrelavant only when seriesUID or seriesId is
// specified and not equals to seriesModel's.
return payload
&& (
payload.seriesUID != null
? seriesModel.uid !== payload.seriesUID
: payload.seriesId != null
// FIXME
// seriesModel.getId() ???
? payload.seriesId !== seriesModel.get('name')
: false
);
},
retrieveTargetNodeInfo: function (payload, seriesModel) {
if (payload && payload.seriesId && seriesModel.uid !== payload.seriesId) {
retrieveTargetInfo: function (payload, seriesModel) {
if (helper.irrelevant(payload, seriesModel)) {
return;
}
if (!payload || payload.type !== 'treemapZoomToNode') {
return;
}
// FIXME
// 从payload中传来node是否合适?
var root = seriesModel.getData().tree.root;
var targetInfo = payload.targetInfo;
if (!targetInfo || !root.contains(targetInfo.node)) {
return;
var targetNode = payload.targetNode;
if (targetNode && root.contains(targetNode)) {
return {node: targetNode};
}
return targetInfo;
var targetNodeId = payload.targetNodeId;
if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) {
return {node: targetNode};
}
return null;
}
};
return helper;
});
\ No newline at end of file
......@@ -32,13 +32,15 @@ define(function (require) {
{},
levelItemStyles,
seriesItemStyleModel,
seriesModel.getViewRoot().getAncestors()
seriesModel.getViewRoot().getAncestors(),
seriesModel
);
});
};
function travelTree(
node, designatedVisual, levelItemStyles, seriesItemStyleModel, viewRootAncestors
node, designatedVisual, levelItemStyles, seriesItemStyleModel,
viewRootAncestors, seriesModel
) {
var nodeModel = node.getModel();
var nodeLayout = node.getLayout();
......@@ -81,9 +83,12 @@ define(function (require) {
if (child.depth >= viewRootAncestors.length
|| child === viewRootAncestors[child.depth]
) {
var childVisual = mapVisual(nodeModel, visuals, child, index, mappingWrap);
var childVisual = mapVisual(
nodeModel, visuals, child, index, mappingWrap, seriesModel
);
travelTree(
child, childVisual, levelItemStyles, seriesItemStyleModel, viewRootAncestors
child, childVisual, levelItemStyles, seriesItemStyleModel,
viewRootAncestors, seriesModel
);
}
});
......@@ -159,15 +164,19 @@ define(function (require) {
return;
}
var colorMapping;
var mappingType = rangeVisual.name === 'color'
? (
nodeModel.get('colorMapping') === 'byValue'
? 'color' : 'colorByIndex'
colorMapping = nodeModel.get('colorMapping'),
colorMapping === 'byValue'
? 'color'
: 'colorByIndex' // When colorMapping is byIndex and byId
)
: rangeVisual.name;
var dataExtent = mappingType === 'colorByIndex'
? null : nodeLayout.dataExtent;
var dataExtent = (
mappingType === 'colorByIndex' || mappingType === 'colorById'
) ? null : nodeLayout.dataExtent;
return {
mapping: new VisualMapping({
......@@ -176,7 +185,8 @@ define(function (require) {
dataNormalizer: 'linear',
visual: rangeVisual.range
}),
visualName: rangeVisual.name
visualName: rangeVisual.name,
colorMapping: colorMapping
};
}
......@@ -194,13 +204,19 @@ define(function (require) {
return (isArray(range) && range.length) ? {name: name, range: range} : null;
}
function mapVisual(nodeModel, visuals, child, index, mappingWrap) {
function mapVisual(nodeModel, visuals, child, index, mappingWrap, seriesModel) {
var childVisuals = zrUtil.extend({}, visuals);
if (mappingWrap) {
var mapping = mappingWrap.mapping;
var value = mapping.type === 'colorByIndex'
? index : child.getValue(nodeModel.get('visualDimension'));
var mappingType = mapping.type;
var value = mappingType === 'colorByIndex'
? (
mappingWrap.colorMapping === 'byId'
? seriesModel.mapIdToIndex(child.getId())
: index // When colorMapping is 'byIndex'
)
: child.getValue(nodeModel.get('visualDimension'));
childVisuals[mappingWrap.visualName] = mapping.mapValueToVisual(value);
}
......
......@@ -8,9 +8,9 @@ define(function(require) {
var echarts = require('../../echarts');
echarts.registerAction('dataZoom', function (event, ecModel) {
echarts.registerAction('dataZoom', function (payload, ecModel) {
var fromDataZoomModel = ecModel.getComponentById(event.dataZoomModelId);
var fromDataZoomModel = ecModel.getComponentById(payload.dataZoomModelId);
if (!fromDataZoomModel) {
return;
}
......@@ -26,7 +26,7 @@ define(function(require) {
var effectedModels = linkedNodesFinder(fromDataZoomModel).nodes;
zrUtil.each(effectedModels, function (dataZoomModel) {
dataZoomModel.setRange(event.dataZoomRange);
dataZoomModel.setRange(payload.dataZoomRange);
});
});
......
......@@ -148,15 +148,15 @@ define(function(require) {
},
/**
* @param {string} name
* @param {string} id
* @return {module:echarts/data/Tree~TreeNode}
*/
getNodeByName: function (name) {
if (this.name === name) {
getNodeById: function (id) {
if (this.getId() === id) {
return this;
}
for (var i = 0, children = this.children, len = children.length; i < len; i++) {
var res = children[i].getNodeByName(name);
var res = children[i].getNodeById(id);
if (res) {
return res;
}
......@@ -265,6 +265,14 @@ define(function(require) {
*/
getRawIndex: function () {
return this.hostTree.data.getRawIndex(this.dataIndex);
},
/**
* @public
* @return {string}
*/
getId: function () {
return this.hostTree.data.getId(this.dataIndex);
}
};
......
define(function (require) {
var textContain = require('zrender/contain/text');
return {
getFont: function () {
return [
......@@ -12,12 +14,18 @@ define(function (require) {
getTextRect: function (text) {
var textStyle = this.get('textStyle') || {};
return require('zrender/contain/text').getBoundingRect(
return textContain.getBoundingRect(
text,
this.getFont(),
textStyle.align,
textStyle.baseline
);
},
ellipsis: function (text, containerWidth, options) {
return textContain.ellipsis(
text, this.getFont(), containerWidth, options
);
}
};
});
\ No newline at end of file
......@@ -7,6 +7,7 @@ define(function(require) {
var numberUtil = require('./number');
var formatUtil = require('./format');
var parsePercent = numberUtil.parsePercent;
var textContain = require('zrender/contain/text');
var layout = {};
......@@ -202,5 +203,21 @@ define(function(require) {
];
};
/**
* @param {string} text
* @param {string} font See textStyle.getFont();
* @param {number} containerWidth
* @param {lineBreak} [lineBreak='\n']
* @return {string} Result text.
*/
layout.textWrap = function (text, font, containerWidth, lineBreak) {
if (!text) {
return '';
}
lineBreak = lineBreak || '\n';
width = textContain.getWidth(text, font);
};
return layout;
});
\ No newline at end of file
......@@ -11,11 +11,11 @@ define(function (require) {
* @param {Object} option
* @param {string} [option.type] See visualHandlers.
* @param {string} [option.dataNormalizer] 'linear' or 'piecewise'
* @param {Array.<number>=} [option.dataExtent=] [minExtent, maxExtent],
* @param {Array.<number>=} [option.dataExtent] [minExtent, maxExtent],
* required when dataNormalizer is 'linear'
* @param {Array.<Array>=} [option.intervals=] [[min1, max1], [min2, max2], ...],
* @param {Array.<Array>=} [option.intervals] [[min1, max1], [min2, max2], ...],
* required when dataNormalizer is 'piecewise'
* @param {Array.<Array>=} [option.intervalVisual=] [value1, value2, ...],
* @param {Array.<Array>=} [option.intervalVisual] [value1, value2, ...],
* specific visual of some interval,
* available when dataNormalizer is 'piecewise'
* @param {Array} [option.visual=] Visual data.
......@@ -24,16 +24,19 @@ define(function (require) {
/**
* @readOnly
* @type {string}
*/
this.type = option.type;
/**
* @readOnly
* @type {Object}
*/
this.option = zrUtil.clone(option, true);
/**
* @private
* @type {Function}
*/
this._normalizeData = dataNormalizers[option.dataNormalizer];
......@@ -62,9 +65,7 @@ define(function (require) {
color: {
applyVisual: function (value, getter, setter) {
setter('color', this.mapValueToVisual(value));
},
applyVisual: defaultApplyColor,
mapValueToVisual: function (value) {
var optionData = this.option.visual;
......@@ -89,9 +90,8 @@ define(function (require) {
},
colorByIndex: {
applyVisual: function (index, getter, setter) {
setter('color', this.mapValueToVisual(index));
},
applyVisual: defaultApplyColor,
mapValueToVisual: function (index) {
var visual = this.option.visual;
......@@ -173,6 +173,10 @@ define(function (require) {
];
}
function defaultApplyColor(value, getter, setter) {
setter('color', this.mapValueToVisual(value));
}
var dataNormalizers = {
linear: function (value) {
......
......@@ -15,7 +15,7 @@
padding: 0;
}
#main {
margin-top: 70px;
margin-top: 90px;
position: absolute;
left: 0;
right: 0;
......@@ -46,7 +46,15 @@
padding-left: 10px;
}
.controller .mode-body {
margin-left: 80px;
float: left;
width: 700px;
}
.controller .query {
margin-left: 800px;
padding-top: 10px;
}
.controller .query #query-input {
width: 250px;
}
.tooltip-title {
color: yellow;
......@@ -58,11 +66,15 @@
<div class="mode-title">示例模式<br>(Mode)</div>
<div class="mode-body">
<input type="radio" id="area-meaning-0" name="area-meaning" onclick="areaMeasureChange(0);" checked="checked"/>
<label for="area-meaning-0">面积代表“2012预算额” (Area represents '2012 Amount)</label><br>
<input type="radio" id="area-meaning-1" name="area-meaning" onclick="areaMeasureChange(1);"/>
<label for="area-meaning-1">面积代表“和2010相比的增长率” (Area represents 'Change from 2010')</label><br>
<label for="area-meaning-0">面积代表“2012年预算额” (Area represents '2012 Amount)</label><br>
<input type="radio" id="area-meaning-2" name="area-meaning" onclick="areaMeasureChange(2);"/>
<label for="area-meaning-2">面积代表“2012预算额”,明暗代表“2010相比的增长率” (Area represents '2012 Amount' and color-alpha represents 'Change from 2010')</label>
<label for="area-meaning-2">面积代表“2012年预算额”,明暗代表“2011年相比的增长率”<br>&nbsp;&nbsp;&nbsp;&nbsp; (Area represents '2012 Amount' and color-alpha represents 'Change from 2011')</label><br>
<input type="radio" id="area-meaning-1" name="area-meaning" onclick="areaMeasureChange(1);"/>
<label for="area-meaning-1">面积代表“2011年预算额” (Area represents '2011 Amount')</label>
</div>
<div class="query">
<input id="query-input" type="text" placeholder="请输入节点名 (Please node name)" onkeypress="if (event.keyCode === 13) { query(); return false; }"/>
<input type="button" class="query-btn" value="检索节点(query)" onclick="query();" />
</div>
</div>
<div id="main"></div>
......@@ -74,6 +86,8 @@
var chart;
var formatUtil;
var SERIES_NAME = 'Obama’s 2012 Budget Proposal: How $3.7 Trillion is Spent';
require([
'echarts',
'echarts/util/format',
......@@ -88,18 +102,61 @@
formatter: getTooltipFormatter(mode)
},
series: [{
visualDimension: mode === 2 ? 'a' : null,
data: buildData(mode),
visualDimension: mode === 2 ? 'b' : null,
data: buildData(mode, window.obama_budget_2012),
levels: getLevelOption(mode)
}]
});
}
function buildData(mode) {
return buildDataRecursively(mode, window.obama_budget_2012);
function query() {
var nodeName = document.getElementById('query-input').value;
// 先找精确匹配的
var nodeIdList = findNodeId(nodeName, window.obama_budget_2012);
// 再找模糊匹配的
if (!nodeIdList.length) {
nodeIdList = findNodeId(nodeName, window.obama_budget_2012, true);
}
if (nodeIdList.length) {
// FIXME
// 接口?
chart.dispatch({
type: 'treemapZoomToNode',
seriesId: SERIES_NAME,
// 这个示例中简单处理,只聚焦到找到的第一个节点上。
targetNodeId: nodeIdList[0]
});
}
else {
alert('没有找到节点 (No result)');
}
}
function buildDataRecursively(mode, originList) {
function findNodeId(nodeName, originList, fuzzy) {
var out = [];
nodeName = nodeName.toLowerCase();
for (var i = 0, len = originList.length; i < len; i++) {
var node = originList[i];
if (node.name
&& (
fuzzy
? node.name.toLowerCase().indexOf(nodeName) === 0
: node.name.toLowerCase() === nodeName
)
) {
out.push(node.id);
}
if (node.children) {
out.push.apply(out, findNodeId(nodeName, node.children, fuzzy));
}
}
return out;
}
function buildData(mode, originList) {
var out = [];
for (var i = 0; i < originList.length; i++) {
......@@ -112,7 +169,7 @@
}
// Calculate amount per household.
value[2] = value[0] / window.household_america_2012;
value[3] = value[0] / window.household_america_2012;
// if mode === 0 and mode === 2 do nothing
if (mode === 1) {
......@@ -123,7 +180,7 @@
}
if (node.children) {
newNode.children = buildDataRecursively(mode, node.children);
newNode.children = buildData(mode, node.children);
}
}
......@@ -153,6 +210,7 @@
'#d4df5a', '#cd4870'
]
: null,
colorMapping: 'byId',
itemStyle: {
normal: {
borderWidth: 3,
......@@ -178,22 +236,27 @@
function getTooltipFormatter(mode) {
var amountIndex = mode === 1 ? 1 : 0;
var changeIndex = mode === 1 ? 0 : 1;
var amountIndex2011 = mode === 1 ? 0 : 1;
return function (info) {
var value = info[0].value;
var amount = value[amountIndex];
amount = isValidNumber(amount)
? formatUtil.addCommas(value[amountIndex] * 1000) + '$'
? formatUtil.addCommas(amount * 1000) + '$'
: '-';
var amount2011 = value[amountIndex2011];
amount2011 = isValidNumber(amount2011)
? formatUtil.addCommas(amount2011 * 1000) + '$'
: '-';
var perHousehold = value[2];
var perHousehold = value[3];
perHousehold = isValidNumber(perHousehold)
? formatUtil.addCommas((+perHousehold.toFixed(4)) * 1000) + '$'
: '-';
var change = value[changeIndex];
var change = value[2];
change = isValidNumber(change)
? change.toFixed(2) + '%'
: '-';
......@@ -202,7 +265,8 @@
'<div class="tooltip-title">' + formatUtil.encodeHTML(info[0].name) + '</div>',
'2012 Amount: &nbsp;&nbsp;' + amount + '<br>',
'Per Household: &nbsp;&nbsp;' + perHousehold + '<br>',
'Change From 2010: &nbsp;&nbsp;' + change
'2011 Amount: &nbsp;&nbsp;' + amount2011 + '<br>',
'Change From 2011: &nbsp;&nbsp;' + change
].join('');
}
}
......@@ -217,9 +281,9 @@
chart.setOption({
legend: {
data: ['Obama’s 2012 Budget Proposal: How $3.7 Trillion is Spent']
},
// legend: {
// data: [SERIES_NAME]
// },
tooltip: {
formatter: getTooltipFormatter(0)
......@@ -227,11 +291,16 @@
series: [
{
name:'Obama’s 2012 Budget Proposal: How $3.7 Trillion is Spent',
type:'treemap',
name: SERIES_NAME,
type: 'treemap',
label: {
show: true,
formatter: "{b}"
formatter: "{b}",
normal: {
textStyle: {
ellipsis: true
}
}
},
itemStyle: {
normal: {
......@@ -239,7 +308,7 @@
}
},
levels: getLevelOption(0),
data: buildData('2012 Amount')
data: buildData('2012 Amount', window.obama_budget_2012)
}
]
});
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册