提交 198e5e93 编写于 作者: L lang

Add funnel

上级 a4718851
define(function (require) {
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
require('./funnel/FunnelSeries');
require('./funnel/FunnelView');
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/dataColor'), 'funnel')
);
echarts.registerLayout(require('./funnel/funnelLayout'));
echarts.registerProcessor(
'filter', zrUtil.curry(require('../processor/dataFilter'), 'funnel')
);
});
\ No newline at end of file
define(function(require) {
'use strict';
var List = require('../../data/List');
var SeriesModel = require('../../model/Series');
var PieSeries = SeriesModel.extend({
type: 'series.funnel',
init: function (option) {
SeriesModel.prototype.init.apply(this, arguments);
// Enable legend selection for each data item
// Use a function instead of direct access because data reference may changed
this.legendDataProvider = function () {
return this._dataBeforeProcessed;
};
},
getInitialData: function (option, ecModel) {
var list = new List(['value'], this);
list.initData(option.data);
return list;
},
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
clickable: true,
legendHoverLink: true,
x: 80,
y: 60,
x2: 80,
y2: 60,
// width: {totalWidth} - x - x2,
// height: {totalHeight} - y - y2,
min: 0,
max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending', // 'ascending', 'descending'
gap: 0,
funnelAlign: 'center',
label: {
normal: {
show: true,
position: 'outer'
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
},
emphasis: {
show: true
}
},
labelLine: {
show: true,
length: 20,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
},
itemStyle: {
normal: {
// color: 各异,
borderColor: '#fff',
borderWidth: 1
},
emphasis: {
// color: 各异,
}
}
}
});
return PieSeries;
});
\ No newline at end of file
define(function (require) {
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
function createLabel(labelLayout) {
return new graphic.Text({
style: {
x: labelLayout.x,
y: labelLayout.y,
textAlign: labelLayout.textAlign,
textBaseline: labelLayout.textBaseline
}
});
}
var Funnel = require('../../view/Chart').extend({
type: 'funnel',
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var oldData = this._data;
var group = this.group;
var enableAnimation = ecModel.get('animation');
var opacityAccessPath = ['itemStyle', 'normal', 'opacity'];
data.diff(oldData)
.add(function (idx) {
var layout = data.getItemLayout(idx);
var itemModel = data.getItemModel();
var poly = new graphic.Polygon({
shape: {
points: layout.points
}
});
var opacity = data.getItemModel(idx).get(opacityAccessPath);
opacity = opacity == null ? 1 : opacity;
if (enableAnimation) {
poly.setStyle({ opacity : 0 });
poly.animateTo({
style: {
opacity: opacity
}
}, 300, 'cubicIn');
}
else {
poly.setStyle({ opacity: opacity });
}
var labelText = createLabel(layout.label);
poly.__labelText = labelText;
if (itemModel.get('labelLine.show')) {
var labelLine = new graphic.Polyline({
shape: {
points: layout.label.linePoints
}
});
group.add(labelLine);
poly.__labelLine = labelLine;
}
group.add(poly);
group.add(labelText);
data.setItemGraphicEl(idx, poly);
})
.update(function (newIdx, oldIdx) {
var poly = oldData.getItemGraphicEl(oldIdx);
api.updateGraphicEl(poly, {
shape: {
points: data.getItemLayout(newIdx).points
}
});
poly.setStyle('opacity', data.getItemModel(newIdx).get(opacityAccessPath));
group.add(poly);
data.setItemGraphicEl(newIdx, poly);
})
.remove(function (idx) {
var poly = oldData.getItemGraphicEl(idx);
enableAnimation
? poly.animateTo({
style: {
opacity: 0
}
}, 300, 'cubicOut', function () {
group.remove(poly);
})
: group.remove(poly);
})
.execute();
this._data = data;
this._updateAll(data, seriesModel);
},
_updateAll: function (data, seriesModel) {
data.eachItemGraphicEl(function (poly, idx) {
var itemModel = data.getItemModel(idx);
var itemStyleModel = itemModel.getModel('itemStyle');
poly.setStyle(
zrUtil.extend(
{
fill: data.getItemVisual(idx, 'color')
},
itemStyleModel.getModel('normal').getItemStyle(['opacity'])
)
);
graphic.setHoverStyle(
poly,
itemStyleModel.getModel('emphasis').getItemStyle()
);
var labelText = poly.__labelText;
if (labelText) {
var textStyleModel = itemModel.getModel('label.normal.textStyle');
labelText.setStyle({
text: seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx),
font: textStyleModel.getFont()
});
}
});
}
});
return Funnel;
});
\ No newline at end of file
define(function (require) {
var layout = require('../../util/layout');
var number = require('../../util/number');
var parsePercent = number.parsePercent;
function getViewRect(seriesModel, api) {
return layout.parsePositionInfo({
x: seriesModel.get('x'),
y: seriesModel.get('y'),
x2: seriesModel.get('x2'),
y2: seriesModel.get('y2'),
width: seriesModel.get('width'),
height: seriesModel.get('height')
}, {
width: api.getWidth(),
height: api.getHeight()
});
}
function getSortedIndices(data, sort) {
var valueArr = data.mapArray('value', function (val) {
return val;
});
var indices = [];
var isAscending = sort === 'ascending';
for (var i = 0, len = data.count(); i < len; i++) {
indices[i] = i;
}
indices.sort(function (a, b) {
return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
});
return indices;
}
function labelLayout (data) {
data.each(function (idx) {
var itemModel = data.getItemModel(idx);
var labelModel = itemModel.getModel('label.normal');
var labelPosition = labelModel.get('position');
var labelLineModel = itemModel.getModel('labelLine');
var layout = data.getItemLayout(idx);
var points = layout.points;
var isLabelInside = labelPosition === 'inner'
|| labelPosition === 'inside' || labelPosition === 'center';
var textAlign;
var textX;
var textY;
var linePoints;
if (isLabelInside) {
textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
}
else {
var x1;
var y1;
var x2;
var labelLineLen = labelLineModel.get('length');
if (labelPosition === 'left') {
// Left side
x1 = (points[3][0] + points[0][0]) / 2;
y1 = (points[3][1] + points[0][1]) / 2;
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
else {
// Right side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'left';
}
var y2 = y1;
linePoints = [[x1, y1], [x2, y2]];
textY = y2;
}
layout.label = {
linePoints: linePoints,
x: textX,
y: textY,
textBaseline: 'middle',
textAlign: textAlign
};
});
}
return function (ecModel, api) {
ecModel.eachSeries(function (seriesModel) {
var data = seriesModel.getData();
var sort = seriesModel.get('sort');
var viewRect = getViewRect(seriesModel, api);
var indices = getSortedIndices(data, sort);
var sizeExtent = [
parsePercent(seriesModel.get('minSize'), viewRect.width),
parsePercent(seriesModel.get('maxSize'), viewRect.width)
];
var dataExtent = data.getDataExtent('value');
dataExtent[0] = 0;
var funnelAlign = seriesModel.get('funnelAlign');
var gap = seriesModel.get('gap');
var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count();
var y = viewRect.y;
var getLinePoints = function (idx, offY) {
var val = data.get('value', idx);
var itemWidth = number.linearMap(val, dataExtent, sizeExtent);
var x0;
switch (funnelAlign) {
case 'left':
x0 = viewRect.x;
break;
case 'center':
x0 = viewRect.x + (viewRect.width - itemWidth) / 2;
break;
case 'right':
x0 = viewRect.x + viewRect.width - itemWidth;
break;
}
return [
[x0, offY],
[x0 + itemWidth, offY]
];
};
if (sort === 'ascending') {
// From bottom to top
itemHeight = -itemHeight;
gap = -gap;
y += viewRect.height;
indices = indices.reverse();
}
for (var i = 0; i < indices.length; i++) {
var idx = indices[i];
var nextIdx = indices[i + 1];
var start = getLinePoints(idx, y);
var end = nextIdx == null
// End point
? [
[viewRect.x + viewRect.width / 2, y + itemHeight],
[viewRect.x + viewRect.width / 2, y + itemHeight]
]
: getLinePoints(nextIdx, y + itemHeight);
y += itemHeight + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
}
labelLayout(data);
});
};
});
\ No newline at end of file
......@@ -6,11 +6,15 @@ define(function (require) {
require('./pie/PieSeries');
require('./pie/PieView');
echarts.registerVisualCoding('chart', require('./pie/pieVisual'));
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/dataColor'), 'pie')
);
echarts.registerLayout(zrUtil.curry(
require('./pie/pieLayout'), 'pie'
));
echarts.registerProcessor('filter', require('./pie/dataItemFilter'));
echarts.registerProcessor(
'filter', zrUtil.curry(require('../processor/dataFilter'), 'pie')
);
});
\ No newline at end of file
......@@ -30,10 +30,7 @@ define(function(require) {
},
getInitialData: function (option, ecModel) {
var list = new List([{
name: 'value',
stackable: true
}], this);
var list = new List(['value'], this);
list.initData(option.data);
return list;
},
......
......@@ -228,12 +228,16 @@ define(function (require) {
);
var labelText = sector.__labelText;
var labelLine = sector.__labelLine;
if (labelText) {
labelText.setStyle({
text: seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx)
});
}
if (labelLine) {
labelLine.setStyle(itemModel.getModel('labelLine').getLineStyle());
}
toggleItemSelected(
sector,
......
......@@ -85,7 +85,7 @@ define(function (require) {
data.each(function (idx) {
var layout = data.getItemLayout(idx);
layout.startAngle = idx * angle;
layout.endAngle = (idx + 1) * angle
layout.endAngle = (idx + 1) * angle;
})
}
else {
......
......@@ -6,5 +6,5 @@ define(function () {
return legendModel.isSelected(series.name);
});
}
}
};
});
\ No newline at end of file
......@@ -12,7 +12,6 @@ define(function (require) {
var BoundingRect = require('zrender/core/BoundingRect');
var v2Copy = vector.copy;
var v2ApplyTransform = vector.applyTransform;
// Dummy transform node
......
......@@ -894,9 +894,7 @@ define(function (require) {
* New list only change the indices.
*/
listProto.cloneShallow = function () {
var dimensionInfoList = zrUtil.map(this.dimensions, function (dim) {
return this._dimensionInfos[dim];
}, this);
var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
var list = new List(dimensionInfoList, this.hostModel);
// FIXME
......
......@@ -676,7 +676,7 @@ define(function (require) {
}
};
echarts.registerVisualCoding('echarts', require('./visual/defaultColor'));
echarts.registerVisualCoding('echarts', require('./visual/seriesColor'));
echarts.registerPreprocessor(require('./preprocessor/backwardCompat'));
......
define(function () {
return function (ecModel) {
return function (seriesType, ecModel) {
var legendModel = ecModel.getComponent('legend');
if (!legendModel) {
return;
}
ecModel.eachSeriesByType('pie', function (series) {
ecModel.eachSeriesByType(seriesType, function (series) {
var data = series.getData();
data.filterSelf(function (idx) {
return legendModel.isSelected(data.getName(idx));
......
// Pick color from palette for each data item
define(function (require) {
return function (ecModel) {
return function (seriesType, ecModel) {
var offset = 0;
var colorList = ecModel.get('color');
ecModel.eachSeriesByTypeAll('pie', function (seriesModel) {
ecModel.eachSeriesByTypeAll(seriesType, function (seriesModel) {
var dataAll = seriesModel.getDataAll();
if (!ecModel.isSeriesFiltered(seriesModel)) {
var data = seriesModel.getData();
......@@ -13,7 +14,7 @@ define(function (require) {
var rawIdx = data.getRawIndex(idx);
var color = itemModel.get('itemStyle.normal.color')
|| colorList[(offset + rawIdx) % colorList.length];
// Legend use the visual info in data before processed
// Legend may use the visual info in data before processed
dataAll.setItemVisual(rawIdx, 'color', color);
data.setItemVisual(idx, 'color', color);
});
......
<html>
<head>
<meta charset="utf-8">
<script src="esl.js"></script>
<script src="config.js"></script>
<script src="lib/dat.gui.min.js"></script>
</head>
<body>
<style>
html, body, #main {
width: 100%;
height: 100%;
margin: 0;
}
</style>
<div id="main"></div>
<script>
require([
'echarts',
'echarts/chart/funnel',
'echarts/component/legend',
'echarts/component/grid',
'echarts/component/tooltip',
'echarts/component/title'
], function (echarts) {
var chart = echarts.init(document.getElementById('main'), null, {
renderer: 'canvas'
});
var itemStyle = {
normal: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowColor: 'rgba(0, 0, 0, 0.4)',
borderWidth: 0
},
emphasis: {
shadowBlur: 40,
shadowOffsetX: 0,
shadowOffsetY: 0,
shadowColor: 'rgba(0, 0, 0, 0.4)',
borderWidth: 2
}
};
chart.setOption({
title : {
text: '漏斗图',
subtext: '纯属虚构'
},
tooltip : {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%'
},
legend: {
data : ['展现','点击','访问','咨询','订单']
},
series: [
{
name:'漏斗图',
type:'funnel',
gap: 3,
itemStyle: itemStyle,
width: '50%',
sort: 'ascending',
data:[
{value:60, name:'访问'},
{value:40, name:'咨询'},
{value:20, name:'订单'},
{value:80, name:'点击'},
{value:100, name:'展现'}
]
}
]
});
var config = {
sort: 'ascending'
};
var gui = new dat.GUI();
gui.add(config, 'sort', ['descending', 'ascending'])
.onChange(function (value) {
chart.setOption({
series: [{
name: 'Les Miserables',
sort: value
}]
});
});
});
</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.
先完成此消息的编辑!
想要评论请 注册