1
0
mirror of https://gitlab.com/JKANetwork/CheckServer.git synced 2026-03-24 11:22:02 +01:00

Start again

This commit is contained in:
2020-10-04 17:14:00 +02:00
parent c0d3912413
commit 091f119048
4382 changed files with 1762543 additions and 9606 deletions

45
vendors/echarts/src/CoordinateSystem.js vendored Normal file
View File

@@ -0,0 +1,45 @@
define(function(require) {
'use strict';
// var zrUtil = require('zrender/core/util');
var coordinateSystemCreators = {};
function CoordinateSystemManager() {
this._coordinateSystems = [];
}
CoordinateSystemManager.prototype = {
constructor: CoordinateSystemManager,
create: function (ecModel, api) {
var coordinateSystems = [];
for (var type in coordinateSystemCreators) {
var list = coordinateSystemCreators[type].create(ecModel, api);
list && (coordinateSystems = coordinateSystems.concat(list));
}
this._coordinateSystems = coordinateSystems;
},
update: function (ecModel, api) {
var coordinateSystems = this._coordinateSystems;
for (var i = 0; i < coordinateSystems.length; i++) {
// FIXME MUST have
coordinateSystems[i].update && coordinateSystems[i].update(ecModel, api);
}
}
};
CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
coordinateSystemCreators[type] = coordinateSystemCreator;
};
CoordinateSystemManager.get = function (type) {
return coordinateSystemCreators[type];
};
return CoordinateSystemManager;
});

19
vendors/echarts/src/ExtensionAPI.js vendored Normal file
View File

@@ -0,0 +1,19 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var echartsAPIList = [
'getDom', 'getZr', 'getWidth', 'getHeight', 'dispatchAction',
'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption'
];
function ExtensionAPI(chartInstance) {
zrUtil.each(echartsAPIList, function (name) {
this[name] = zrUtil.bind(chartInstance[name], chartInstance);
}, this);
}
return ExtensionAPI;
});

View File

@@ -0,0 +1,35 @@
define(function (require) {
var echarts = require('../echarts');
var zrUtil = require('zrender/core/util');
return function (seriesType, actionInfos) {
zrUtil.each(actionInfos, function (actionInfo) {
actionInfo.update = 'updateView';
/**
* @payload
* @property {string} seriesName
* @property {string} name
*/
echarts.registerAction(actionInfo, function (payload, ecModel) {
var selected = {};
ecModel.eachComponent(
{mainType: 'series', subType: seriesType, query: payload},
function (seriesModel) {
if (seriesModel[actionInfo.method]) {
seriesModel[actionInfo.method](payload.name);
}
var data = seriesModel.getData();
// Create selected map
data.each(function (idx) {
var name = data.getName(idx);
selected[name] = seriesModel.isSelected(name) || false;
});
}
);
return {
name: payload.name,
selected: selected
};
});
});
};
});

53
vendors/echarts/src/action/geoRoam.js vendored Normal file
View File

@@ -0,0 +1,53 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var roamHelper = require('./roamHelper');
var echarts = require('../echarts');
/**
* @payload
* @property {string} [componentType=series]
* @property {number} [dx]
* @property {number} [dy]
* @property {number} [zoom]
* @property {number} [originX]
* @property {number} [originY]
*/
echarts.registerAction({
type: 'geoRoam',
event: 'geoRoam',
update: 'updateLayout'
}, function (payload, ecModel) {
var componentType = payload.componentType || 'series';
ecModel.eachComponent(
{ mainType: componentType, query: payload },
function (componentModel) {
var geo = componentModel.coordinateSystem;
if (geo.type !== 'geo') {
return;
}
var res = roamHelper.updateCenterAndZoom(
geo, payload, componentModel.get('scaleLimit')
);
componentModel.setCenter
&& componentModel.setCenter(res.center);
componentModel.setZoom
&& componentModel.setZoom(res.zoom);
// All map series with same `map` use the same geo coordinate system
// So the center and zoom must be in sync. Include the series not selected by legend
if (componentType === 'series') {
zrUtil.each(componentModel.seriesGroup, function (seriesModel) {
seriesModel.setCenter(res.center);
seriesModel.setZoom(res.zoom);
});
}
}
);
});
});

View File

@@ -0,0 +1,60 @@
define(function (require) {
var roamHelper = {};
/**
* @param {module:echarts/coord/View} view
* @param {Object} payload
* @param {Object} [zoomLimit]
*/
roamHelper.updateCenterAndZoom = function (
view, payload, zoomLimit
) {
var previousZoom = view.getZoom();
var center = view.getCenter();
var zoom = payload.zoom;
var point = view.dataToPoint(center);
if (payload.dx != null && payload.dy != null) {
point[0] -= payload.dx;
point[1] -= payload.dy;
var center = view.pointToData(point);
view.setCenter(center);
}
if (zoom != null) {
if (zoomLimit) {
var zoomMin = zoomLimit.min || 0;
var zoomMax = zoomLimit.max || Infinity;
zoom = Math.max(
Math.min(previousZoom * zoom, zoomMax),
zoomMin
) / previousZoom;
}
// Zoom on given point(originX, originY)
view.scale[0] *= zoom;
view.scale[1] *= zoom;
var position = view.position;
var fixX = (payload.originX - position[0]) * (zoom - 1);
var fixY = (payload.originY - position[1]) * (zoom - 1);
position[0] -= fixX;
position[1] -= fixY;
view.updateTransform();
// Get the new center
var center = view.pointToData(point);
view.setCenter(center);
view.setZoom(zoom * previousZoom);
}
return {
center: view.getCenter(),
zoom: view.getZoom()
};
};
return roamHelper;
});

24
vendors/echarts/src/chart/bar.js vendored Normal file
View File

@@ -0,0 +1,24 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
require('../coord/cartesian/Grid');
require('./bar/BarSeries');
require('./bar/BarView');
var barLayoutGrid = require('../layout/barGrid');
var echarts = require('../echarts');
echarts.registerLayout(zrUtil.curry(barLayoutGrid, 'bar'));
// Visual coding for legend
echarts.registerVisualCoding('chart', function (ecModel) {
ecModel.eachSeriesByType('bar', function (seriesModel) {
var data = seriesModel.getData();
data.setVisual('legendSymbol', 'roundRect');
});
});
// In case developer forget to include grid component
require('../component/grid');
});

View File

@@ -0,0 +1,76 @@
define(function(require) {
'use strict';
var SeriesModel = require('../../model/Series');
var createListFromArray = require('../helper/createListFromArray');
return SeriesModel.extend({
type: 'series.bar',
dependencies: ['grid', 'polar'],
getInitialData: function (option, ecModel) {
return createListFromArray(option.data, this, ecModel);
},
getMarkerPosition: function (value) {
var coordSys = this.coordinateSystem;
if (coordSys) {
var pt = coordSys.dataToPoint(value);
var data = this.getData();
var offset = data.getLayout('offset');
var size = data.getLayout('size');
var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1;
pt[offsetIndex] += offset + size / 2;
return pt;
}
return [NaN, NaN];
},
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
coordinateSystem: 'cartesian2d',
legendHoverLink: true,
// stack: null
// Cartesian coordinate system
xAxisIndex: 0,
yAxisIndex: 0,
// 最小高度改为0
barMinHeight: 0,
// barMaxWidth: null,
// 默认自适应
// barWidth: null,
// 柱间距离默认为柱形宽度的30%,可设固定值
// barGap: '30%',
// 类目间柱形距离默认为类目间距的20%,可设固定值
// barCategoryGap: '20%',
// label: {
// normal: {
// show: false
// }
// },
itemStyle: {
normal: {
// color: '各异',
// 柱条边线
barBorderColor: '#fff',
// 柱条边线线宽单位px默认为1
barBorderWidth: 0
},
emphasis: {
// color: '各异',
// 柱条边线
barBorderColor: '#fff',
// 柱条边线线宽单位px默认为1
barBorderWidth: 0
}
}
}
});
});

215
vendors/echarts/src/chart/bar/BarView.js vendored Normal file
View File

@@ -0,0 +1,215 @@
define(function (require) {
'use strict';
var zrUtil = require('zrender/core/util');
var graphic = require('../../util/graphic');
zrUtil.extend(require('../../model/Model').prototype, require('./barItemStyle'));
function fixLayoutWithLineWidth(layout, lineWidth) {
var signX = layout.width > 0 ? 1 : -1;
var signY = layout.height > 0 ? 1 : -1;
// In case width or height are too small.
lineWidth = Math.min(lineWidth, Math.abs(layout.width), Math.abs(layout.height));
layout.x += signX * lineWidth / 2;
layout.y += signY * lineWidth / 2;
layout.width -= signX * lineWidth;
layout.height -= signY * lineWidth;
}
return require('../../echarts').extendChartView({
type: 'bar',
render: function (seriesModel, ecModel, api) {
var coordinateSystemType = seriesModel.get('coordinateSystem');
if (coordinateSystemType === 'cartesian2d') {
this._renderOnCartesian(seriesModel, ecModel, api);
}
return this.group;
},
_renderOnCartesian: function (seriesModel, ecModel, api) {
var group = this.group;
var data = seriesModel.getData();
var oldData = this._data;
var cartesian = seriesModel.coordinateSystem;
var baseAxis = cartesian.getBaseAxis();
var isHorizontal = baseAxis.isHorizontal();
var enableAnimation = seriesModel.get('animation');
var barBorderWidthQuery = ['itemStyle', 'normal', 'barBorderWidth'];
function createRect(dataIndex, isUpdate) {
var layout = data.getItemLayout(dataIndex);
var lineWidth = data.getItemModel(dataIndex).get(barBorderWidthQuery) || 0;
fixLayoutWithLineWidth(layout, lineWidth);
var rect = new graphic.Rect({
shape: zrUtil.extend({}, layout)
});
// Animation
if (enableAnimation) {
var rectShape = rect.shape;
var animateProperty = isHorizontal ? 'height' : 'width';
var animateTarget = {};
rectShape[animateProperty] = 0;
animateTarget[animateProperty] = layout[animateProperty];
graphic[isUpdate? 'updateProps' : 'initProps'](rect, {
shape: animateTarget
}, seriesModel, dataIndex);
}
return rect;
}
data.diff(oldData)
.add(function (dataIndex) {
// 空数据
if (!data.hasValue(dataIndex)) {
return;
}
var rect = createRect(dataIndex);
data.setItemGraphicEl(dataIndex, rect);
group.add(rect);
})
.update(function (newIndex, oldIndex) {
var rect = oldData.getItemGraphicEl(oldIndex);
// 空数据
if (!data.hasValue(newIndex)) {
group.remove(rect);
return;
}
if (!rect) {
rect = createRect(newIndex, true);
}
var layout = data.getItemLayout(newIndex);
var lineWidth = data.getItemModel(newIndex).get(barBorderWidthQuery) || 0;
fixLayoutWithLineWidth(layout, lineWidth);
graphic.updateProps(rect, {
shape: layout
}, seriesModel, newIndex);
data.setItemGraphicEl(newIndex, rect);
// Add back
group.add(rect);
})
.remove(function (idx) {
var rect = oldData.getItemGraphicEl(idx);
if (rect) {
// Not show text when animating
rect.style.text = '';
graphic.updateProps(rect, {
shape: {
width: 0
}
}, seriesModel, idx, function () {
group.remove(rect);
});
}
})
.execute();
this._updateStyle(seriesModel, data, isHorizontal);
this._data = data;
},
_updateStyle: function (seriesModel, data, isHorizontal) {
function setLabel(style, model, color, labelText, labelPositionOutside) {
graphic.setText(style, model, color);
style.text = labelText;
if (style.textPosition === 'outside') {
style.textPosition = labelPositionOutside;
}
}
data.eachItemGraphicEl(function (rect, idx) {
var itemModel = data.getItemModel(idx);
var color = data.getItemVisual(idx, 'color');
var opacity = data.getItemVisual(idx, 'opacity');
var layout = data.getItemLayout(idx);
var itemStyleModel = itemModel.getModel('itemStyle.normal');
var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle();
rect.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
rect.useStyle(zrUtil.defaults(
{
fill: color,
opacity: opacity
},
itemStyleModel.getBarItemStyle()
));
var labelPositionOutside = isHorizontal
? (layout.height > 0 ? 'bottom' : 'top')
: (layout.width > 0 ? 'left' : 'right');
var labelModel = itemModel.getModel('label.normal');
var hoverLabelModel = itemModel.getModel('label.emphasis');
var rectStyle = rect.style;
if (labelModel.get('show')) {
setLabel(
rectStyle, labelModel, color,
zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'normal'),
seriesModel.getRawValue(idx)
),
labelPositionOutside
);
}
else {
rectStyle.text = '';
}
if (hoverLabelModel.get('show')) {
setLabel(
hoverStyle, hoverLabelModel, color,
zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'emphasis'),
seriesModel.getRawValue(idx)
),
labelPositionOutside
);
}
else {
hoverStyle.text = '';
}
graphic.setHoverStyle(rect, hoverStyle);
});
},
remove: function (ecModel, api) {
var group = this.group;
if (ecModel.get('animation')) {
if (this._data) {
this._data.eachItemGraphicEl(function (el) {
// Not show text when animating
el.style.text = '';
graphic.updateProps(el, {
shape: {
width: 0
}
}, ecModel, el.dataIndex, function () {
group.remove(el);
});
});
}
}
else {
group.removeAll();
}
}
});
});

View File

@@ -0,0 +1,19 @@
define(function (require) {
return {
getBarItemStyle: require('../../model/mixin/makeStyleMapper')(
[
['fill', 'color'],
['stroke', 'borderColor'],
['lineWidth', 'borderWidth'],
// Compatitable with 2
['stroke', 'barBorderColor'],
['lineWidth', 'barBorderWidth'],
['opacity'],
['shadowBlur'],
['shadowOffsetX'],
['shadowOffsetY'],
['shadowColor']
]
)
};
});

11
vendors/echarts/src/chart/boxplot.js vendored Normal file
View File

@@ -0,0 +1,11 @@
define(function (require) {
var echarts = require('../echarts');
require('./boxplot/BoxplotSeries');
require('./boxplot/BoxplotView');
echarts.registerVisualCoding('chart', require('./boxplot/boxplotVisual'));
echarts.registerLayout(require('./boxplot/boxplotLayout'));
});

View File

@@ -0,0 +1,72 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var SeriesModel = require('../../model/Series');
var whiskerBoxCommon = require('../helper/whiskerBoxCommon');
var BoxplotSeries = SeriesModel.extend({
type: 'series.boxplot',
dependencies: ['xAxis', 'yAxis', 'grid'],
// TODO
// box width represents group size, so dimension should have 'size'.
/**
* @see <https://en.wikipedia.org/wiki/Box_plot>
* The meanings of 'min' and 'max' depend on user,
* and echarts do not need to know it.
* @readOnly
*/
valueDimensions: ['min', 'Q1', 'median', 'Q3', 'max'],
/**
* @type {Array.<string>}
* @readOnly
*/
dimensions: null,
/**
* @override
*/
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
coordinateSystem: 'cartesian2d',
legendHoverLink: true,
hoverAnimation: true,
xAxisIndex: 0,
yAxisIndex: 0,
layout: null, // 'horizontal' or 'vertical'
boxWidth: [7, 50], // [min, max] can be percent of band width.
itemStyle: {
normal: {
color: '#fff',
borderWidth: 1
},
emphasis: {
borderWidth: 2,
shadowBlur: 5,
shadowOffsetX: 2,
shadowOffsetY: 2,
shadowColor: 'rgba(0,0,0,0.4)'
}
},
animationEasing: 'elasticOut',
animationDuration: 800
}
});
zrUtil.mixin(BoxplotSeries, whiskerBoxCommon.seriesModelMixin, true);
return BoxplotSeries;
});

View File

@@ -0,0 +1,49 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var ChartView = require('../../view/Chart');
var graphic = require('../../util/graphic');
var whiskerBoxCommon = require('../helper/whiskerBoxCommon');
var BoxplotView = ChartView.extend({
type: 'boxplot',
getStyleUpdater: function () {
return updateStyle;
}
});
zrUtil.mixin(BoxplotView, whiskerBoxCommon.viewMixin, true);
// Update common properties
var normalStyleAccessPath = ['itemStyle', 'normal'];
var emphasisStyleAccessPath = ['itemStyle', 'emphasis'];
function updateStyle(itemGroup, data, idx) {
var itemModel = data.getItemModel(idx);
var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath);
var borderColor = data.getItemVisual(idx, 'color');
// Exclude borderColor.
var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']);
var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex);
whiskerEl.style.set(itemStyle);
whiskerEl.style.stroke = borderColor;
whiskerEl.dirty();
var bodyEl = itemGroup.childAt(itemGroup.bodyIndex);
bodyEl.style.set(itemStyle);
bodyEl.style.stroke = borderColor;
bodyEl.dirty();
var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
graphic.setHoverStyle(itemGroup, hoverStyle);
}
return BoxplotView;
});

View File

@@ -0,0 +1,181 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var numberUtil = require('../../util/number');
var parsePercent = numberUtil.parsePercent;
var each = zrUtil.each;
return function (ecModel, api) {
var groupResult = groupSeriesByAxis(ecModel);
each(groupResult, function (groupItem) {
var seriesModels = groupItem.seriesModels;
if (!seriesModels.length) {
return;
}
calculateBase(groupItem);
each(seriesModels, function (seriesModel, idx) {
layoutSingleSeries(
seriesModel,
groupItem.boxOffsetList[idx],
groupItem.boxWidthList[idx]
);
});
});
};
/**
* Group series by axis.
*/
function groupSeriesByAxis(ecModel) {
var result = [];
var axisList = [];
ecModel.eachSeriesByType('boxplot', function (seriesModel) {
var baseAxis = seriesModel.getBaseAxis();
var idx = zrUtil.indexOf(axisList, baseAxis);
if (idx < 0) {
idx = axisList.length;
axisList[idx] = baseAxis;
result[idx] = {axis: baseAxis, seriesModels: []};
}
result[idx].seriesModels.push(seriesModel);
});
return result;
}
/**
* Calculate offset and box width for each series.
*/
function calculateBase(groupItem) {
var extent;
var baseAxis = groupItem.axis;
var seriesModels = groupItem.seriesModels;
var seriesCount = seriesModels.length;
var boxWidthList = groupItem.boxWidthList = [];
var boxOffsetList = groupItem.boxOffsetList = [];
var boundList = [];
var bandWidth;
if (baseAxis.type === 'category') {
bandWidth = baseAxis.getBandWidth();
}
else {
var maxDataCount = 0;
each(seriesModels, function (seriesModel) {
maxDataCount = Math.max(maxDataCount, seriesModel.getData().count());
});
extent = baseAxis.getExtent(),
Math.abs(extent[1] - extent[0]) / maxDataCount;
}
each(seriesModels, function (seriesModel) {
var boxWidthBound = seriesModel.get('boxWidth');
if (!zrUtil.isArray(boxWidthBound)) {
boxWidthBound = [boxWidthBound, boxWidthBound];
}
boundList.push([
parsePercent(boxWidthBound[0], bandWidth) || 0,
parsePercent(boxWidthBound[1], bandWidth) || 0
]);
});
var availableWidth = bandWidth * 0.8 - 2;
var boxGap = availableWidth / seriesCount * 0.3;
var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount;
var base = boxWidth / 2 - availableWidth / 2;
each(seriesModels, function (seriesModel, idx) {
boxOffsetList.push(base);
base += boxGap + boxWidth;
boxWidthList.push(
Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1])
);
});
}
/**
* Calculate points location for each series.
*/
function layoutSingleSeries(seriesModel, offset, boxWidth) {
var coordSys = seriesModel.coordinateSystem;
var data = seriesModel.getData();
var dimensions = seriesModel.dimensions;
var chartLayout = seriesModel.get('layout');
var halfWidth = boxWidth / 2;
data.each(dimensions, function () {
var args = arguments;
var dimLen = dimensions.length;
var axisDimVal = args[0];
var idx = args[dimLen];
var variableDim = chartLayout === 'horizontal' ? 0 : 1;
var constDim = 1 - variableDim;
var median = getPoint(args[3]);
var end1 = getPoint(args[1]);
var end5 = getPoint(args[5]);
var whiskerEnds = [
[end1, getPoint(args[2])],
[end5, getPoint(args[4])]
];
layEndLine(end1);
layEndLine(end5);
layEndLine(median);
var bodyEnds = [];
addBodyEnd(whiskerEnds[0][1], 0);
addBodyEnd(whiskerEnds[1][1], 1);
data.setItemLayout(idx, {
chartLayout: chartLayout,
initBaseline: median[constDim],
median: median,
bodyEnds: bodyEnds,
whiskerEnds: whiskerEnds
});
function getPoint(val) {
var p = [];
p[variableDim] = axisDimVal;
p[constDim] = val;
var point;
if (isNaN(axisDimVal) || isNaN(val)) {
point = [NaN, NaN];
}
else {
point = coordSys.dataToPoint(p);
point[variableDim] += offset;
}
return point;
}
function addBodyEnd(point, start) {
var point1 = point.slice();
var point2 = point.slice();
point1[variableDim] += halfWidth;
point2[variableDim] -= halfWidth;
start
? bodyEnds.push(point1, point2)
: bodyEnds.push(point2, point1);
}
function layEndLine(endCenter) {
var line = [endCenter.slice(), endCenter.slice()];
line[0][variableDim] -= halfWidth;
line[1][variableDim] += halfWidth;
whiskerEnds.push(line);
}
});
}
});

View File

@@ -0,0 +1,34 @@
define(function (require) {
var borderColorQuery = ['itemStyle', 'normal', 'borderColor'];
return function (ecModel, api) {
var globalColors = ecModel.get('color');
ecModel.eachRawSeriesByType('boxplot', function (seriesModel) {
var defaulColor = globalColors[seriesModel.seriesIndex % globalColors.length];
var data = seriesModel.getData();
data.setVisual({
legendSymbol: 'roundRect',
// Use name 'color' but not 'borderColor' for legend usage and
// visual coding from other component like dataRange.
color: seriesModel.get(borderColorQuery) || defaulColor
});
// Only visible series has each data be visual encoded
if (!ecModel.isSeriesFiltered(seriesModel)) {
data.each(function (idx) {
var itemModel = data.getItemModel(idx);
data.setItemVisual(
idx,
{color: itemModel.get(borderColorQuery, true)}
);
});
}
});
};
});

View File

@@ -0,0 +1,15 @@
define(function (require) {
var echarts = require('../echarts');
require('./candlestick/CandlestickSeries');
require('./candlestick/CandlestickView');
echarts.registerPreprocessor(
require('./candlestick/preprocessor')
);
echarts.registerVisualCoding('chart', require('./candlestick/candlestickVisual'));
echarts.registerLayout(require('./candlestick/candlestickLayout'));
});

View File

@@ -0,0 +1,92 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var SeriesModel = require('../../model/Series');
var whiskerBoxCommon = require('../helper/whiskerBoxCommon');
var formatUtil = require('../../util/format');
var encodeHTML = formatUtil.encodeHTML;
var addCommas = formatUtil.addCommas;
var CandlestickSeries = SeriesModel.extend({
type: 'series.candlestick',
dependencies: ['xAxis', 'yAxis', 'grid'],
/**
* @readOnly
*/
valueDimensions: ['open', 'close', 'lowest', 'highest'],
/**
* @type {Array.<string>}
* @readOnly
*/
dimensions: null,
/**
* @override
*/
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
coordinateSystem: 'cartesian2d',
legendHoverLink: true,
hoverAnimation: true,
xAxisIndex: 0,
yAxisIndex: 0,
layout: null, // 'horizontal' or 'vertical'
itemStyle: {
normal: {
color: '#c23531', // 阳线 positive
color0: '#314656', // 阴线 negative '#c23531', '#314656'
borderWidth: 1,
// FIXME
// ec2中使用的是lineStyle.color 和 lineStyle.color0
borderColor: '#c23531',
borderColor0: '#314656'
},
emphasis: {
borderWidth: 2
}
},
animationUpdate: false,
animationEasing: 'linear',
animationDuration: 300
},
/**
* Get dimension for shadow in dataZoom
* @return {string} dimension name
*/
getShadowDim: function () {
return 'open';
},
/**
* @override
*/
formatTooltip: function (dataIndex, mutipleSeries) {
// It rearly use mutiple candlestick series in one cartesian,
// so only consider one series in this default tooltip.
var valueHTMLArr = zrUtil.map(this.valueDimensions, function (dim) {
return dim + ': ' + addCommas(this._data.get(dim, dataIndex));
}, this);
return encodeHTML(this.name) + '<br />' + valueHTMLArr.join('<br />');
}
});
zrUtil.mixin(CandlestickSeries, whiskerBoxCommon.seriesModelMixin, true);
return CandlestickSeries;
});

View File

@@ -0,0 +1,54 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var ChartView = require('../../view/Chart');
var graphic = require('../../util/graphic');
var whiskerBoxCommon = require('../helper/whiskerBoxCommon');
var CandlestickView = ChartView.extend({
type: 'candlestick',
getStyleUpdater: function () {
return updateStyle;
}
});
zrUtil.mixin(CandlestickView, whiskerBoxCommon.viewMixin, true);
// Update common properties
var normalStyleAccessPath = ['itemStyle', 'normal'];
var emphasisStyleAccessPath = ['itemStyle', 'emphasis'];
function updateStyle(itemGroup, data, idx) {
var itemModel = data.getItemModel(idx);
var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath);
var color = data.getItemVisual(idx, 'color');
var borderColor = data.getItemVisual(idx, 'borderColor');
// Color must be excluded.
// Because symbol provide setColor individually to set fill and stroke
var itemStyle = normalItemStyleModel.getItemStyle(
['color', 'color0', 'borderColor', 'borderColor0']
);
var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex);
whiskerEl.useStyle(itemStyle);
whiskerEl.style.stroke = borderColor;
var bodyEl = itemGroup.childAt(itemGroup.bodyIndex);
bodyEl.useStyle(itemStyle);
bodyEl.style.fill = color;
bodyEl.style.stroke = borderColor;
var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
graphic.setHoverStyle(itemGroup, hoverStyle);
}
return CandlestickView;
});

View File

@@ -0,0 +1,103 @@
define(function (require) {
var CANDLE_MIN_WIDTH = 2;
var CANDLE_MIN_NICE_WIDTH = 5;
var GPA_MIN = 4;
return function (ecModel, api) {
ecModel.eachSeriesByType('candlestick', function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
var data = seriesModel.getData();
var dimensions = seriesModel.dimensions;
var chartLayout = seriesModel.get('layout');
var candleWidth = calculateCandleWidth(seriesModel, data);
data.each(dimensions, function () {
var args = arguments;
var dimLen = dimensions.length;
var axisDimVal = args[0];
var idx = args[dimLen];
var variableDim = chartLayout === 'horizontal' ? 0 : 1;
var constDim = 1 - variableDim;
var openVal = args[1];
var closeVal = args[2];
var lowestVal = args[3];
var highestVal = args[4];
var ocLow = Math.min(openVal, closeVal);
var ocHigh = Math.max(openVal, closeVal);
var ocLowPoint = getPoint(ocLow);
var ocHighPoint = getPoint(ocHigh);
var lowestPoint = getPoint(lowestVal);
var highestPoint = getPoint(highestVal);
var whiskerEnds = [
[highestPoint, ocHighPoint],
[lowestPoint, ocLowPoint]
];
var bodyEnds = [];
addBodyEnd(ocHighPoint, 0);
addBodyEnd(ocLowPoint, 1);
data.setItemLayout(idx, {
chartLayout: chartLayout,
sign: openVal > closeVal ? -1 : openVal < closeVal ? 1 : 0,
initBaseline: openVal > closeVal
? ocHighPoint[constDim] : ocLowPoint[constDim], // open point.
bodyEnds: bodyEnds,
whiskerEnds: whiskerEnds
});
function getPoint(val) {
var p = [];
p[variableDim] = axisDimVal;
p[constDim] = val;
return (isNaN(axisDimVal) || isNaN(val))
? [NaN, NaN]
: coordSys.dataToPoint(p);
}
function addBodyEnd(point, start) {
var point1 = point.slice();
var point2 = point.slice();
point1[variableDim] += candleWidth / 2;
point2[variableDim] -= candleWidth / 2;
start
? bodyEnds.push(point1, point2)
: bodyEnds.push(point2, point1);
}
}, true);
});
};
function calculateCandleWidth(seriesModel, data) {
var baseAxis = seriesModel.getBaseAxis();
var extent;
var bandWidth = baseAxis.type === 'category'
? baseAxis.getBandWidth()
: (
extent = baseAxis.getExtent(),
Math.abs(extent[1] - extent[0]) / data.count()
);
// Half band width is perfect when space is enouph, otherwise
// try not to be smaller than CANDLE_MIN_NICE_WIDTH (and only
// gap is compressed), otherwise ensure not to be smaller than
// CANDLE_MIN_WIDTH in spite of overlap.
return bandWidth / 2 - 2 > CANDLE_MIN_NICE_WIDTH // "- 2" is minus border width
? bandWidth / 2 - 2
: bandWidth - CANDLE_MIN_NICE_WIDTH > GPA_MIN
? CANDLE_MIN_NICE_WIDTH
: Math.max(bandWidth - GPA_MIN, CANDLE_MIN_WIDTH);
}
});

View File

@@ -0,0 +1,40 @@
define(function (require) {
var positiveBorderColorQuery = ['itemStyle', 'normal', 'borderColor'];
var negativeBorderColorQuery = ['itemStyle', 'normal', 'borderColor0'];
var positiveColorQuery = ['itemStyle', 'normal', 'color'];
var negativeColorQuery = ['itemStyle', 'normal', 'color0'];
return function (ecModel, api) {
ecModel.eachRawSeriesByType('candlestick', function (seriesModel) {
var data = seriesModel.getData();
data.setVisual({
legendSymbol: 'roundRect'
});
// Only visible series has each data be visual encoded
if (!ecModel.isSeriesFiltered(seriesModel)) {
data.each(function (idx) {
var itemModel = data.getItemModel(idx);
var sign = data.getItemLayout(idx).sign;
data.setItemVisual(
idx,
{
color: itemModel.get(
sign > 0 ? positiveColorQuery : negativeColorQuery
),
borderColor: itemModel.get(
sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery
)
}
);
});
}
});
};
});

View File

@@ -0,0 +1,18 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
return function (option) {
if (!option || !zrUtil.isArray(option.series)) {
return;
}
// Translate 'k' to 'candlestick'.
zrUtil.each(option.series, function (seriesItem) {
if (zrUtil.isObject(seriesItem) && seriesItem.type === 'k') {
seriesItem.type = 'candlestick';
}
});
};
});

17
vendors/echarts/src/chart/chord.js vendored Normal file
View File

@@ -0,0 +1,17 @@
define(function (require) {
require('./chord/ChordSeries');
require('./chord/ChordView');
var echarts = require('../echarts');
var zrUtil = require('zrender/core/util');
echarts.registerLayout(require('./chord/chordCircularLayout'));
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/dataColor'), 'chord')
);
echarts.registerProcessor(
'filter', zrUtil.curry(require('../processor/dataFilter'), 'pie')
);
});

View File

@@ -0,0 +1,64 @@
define(function (require) {
var SeriesModel = require('../../model/Series');
var createGraphFromNodeEdge = require('../helper/createGraphFromNodeEdge');
var createGraphFromNodeMatrix = require('../helper/createGraphFromNodeMatrix');
var ChordSeries = SeriesModel.extend({
type: 'series.chord',
getInitialData: function (option) {
var edges = option.edges || option.links;
var nodes = option.data || option.nodes;
var matrix = option.matrix;
if (nodes && edges) {
var graph = createGraphFromNodeEdge(nodes, edges, this, true);
return graph.data;
}
else if (nodes && matrix) {
var graph = createGraphFromNodeMatrix(nodes, matrix, this, true);
return graph.data;
}
},
/**
* @return {module:echarts/data/Graph}
*/
getGraph: function () {
return this.getData().graph;
},
/**
* @return {module:echarts/data/List}
*/
getEdgeData: function () {
return this.getGraph().edgeData;
},
defaultOption: {
center: ['50%', '50%'],
radius: ['65%', '75%'],
//
// layout: 'circular',
sort: 'none',
sortSub: 'none',
padding: 0.02,
startAngle: 90,
clockwise: true,
itemStyle: {
normal: {},
emphasis: {}
},
chordStyle: {
normal: {},
emphasis: {}
}
}
});
return ChordSeries;
});

View File

@@ -0,0 +1,75 @@
define(function (require) {
var RibbonPath = require('./Ribbon');
var graphic = require('../../util/graphic');
return require('../../echarts').extendChartView({
type: 'chord',
init: function (option) {
},
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var graph = seriesModel.getGraph();
var edgeData = seriesModel.getEdgeData();
var group = this.group;
group.removeAll();
data.each(function (idx) {
var layout = data.getItemLayout(idx);
var sector = new graphic.Sector({
shape: {
cx: layout.cx,
cy: layout.cy,
clockwise: layout.clockwise,
r0: layout.r0,
r: layout.r,
startAngle: layout.startAngle,
endAngle: layout.endAngle
}
});
sector.setStyle({
fill: data.getItemVisual(idx, 'color')
});
data.setItemLayout(idx);
group.add(sector);
});
var edgeRendered = {};
edgeData.each(function (idx) {
if (edgeRendered[idx]) {
return;
}
var layout = edgeData.getItemLayout(idx);
var edge = graph.getEdgeByIndex(idx);
var otherEdge = graph.getEdge(edge.node2, edge.node1);
var otherEdgeLayout = otherEdge.getLayout();
edgeRendered[idx] = edgeRendered[otherEdge.dataIndex] = true;
var ribbon = new RibbonPath({
shape: {
cx: layout.cx,
cy: layout.cy,
r: layout.r,
s0: layout.startAngle,
s1: layout.endAngle,
t0: otherEdgeLayout.startAngle,
t1: otherEdgeLayout.endAngle,
clockwise: layout.clockwise
}
});
ribbon.setStyle({
// Use color of source
fill: edge.node1.getVisual('color'),
opacity: 0.5
});
group.add(ribbon);
});
}
});
});

View File

@@ -0,0 +1,67 @@
define(function (require) {
var sin = Math.sin;
var cos = Math.cos;
return require('../../util/graphic').extendShape({
type: 'ec-ribbon',
shape: {
cx: 0,
cy: 0,
r: 0,
s0: 0,
s1: 0,
t0: 0,
t1: 0
},
style: {
fill: '#000'
},
buildPath: function (ctx, shape) {
var clockwise = shape.clockwise || false;
var cx = shape.cx;
var cy = shape.cy;
var r = shape.r;
var s0 = shape.s0;
var s1 = shape.s1;
var t0 = shape.t0;
var t1 = shape.t1;
var sx0 = cx + cos(s0) * r;
var sy0 = cy + sin(s0) * r;
var sx1 = cx + cos(s1) * r;
var sy1 = cy + sin(s1) * r;
var tx0 = cx + cos(t0) * r;
var ty0 = cy + sin(t0) * r;
var tx1 = cx + cos(t1) * r;
var ty1 = cy + sin(t1) * r;
ctx.moveTo(sx0, sy0);
ctx.arc(cx, cy, shape.r, s0, s1, !clockwise);
ctx.bezierCurveTo(
(cx - sx1) * 0.70 + sx1,
(cy - sy1) * 0.70 + sy1,
(cx - tx0) * 0.70 + tx0,
(cy - ty0) * 0.70 + ty0,
tx0, ty0
);
// Chord to self
if (shape.s0 === shape.t0 && shape.s1 === shape.t1) {
return;
}
ctx.arc(cx, cy, shape.r, t0, t1, !clockwise);
ctx.bezierCurveTo(
(cx - tx1) * 0.70 + tx1,
(cy - ty1) * 0.70 + ty1,
(cx - sx0) * 0.70 + sx0,
(cy - sy0) * 0.70 + sy0,
sx0, sy0
);
}
});
});

View File

@@ -0,0 +1,123 @@
/**
* Chord layout
* @module echarts/chart/chord/chordCircularLayout
* @author pissang(http://github.com/pissang)
*/
define(function (require) {
var zrUtil = require('zrender/core/util');
var numberUtil = require('../../util/number');
/**
* @param {module:echarts/data/Graph} graph
*/
function layout(graphs, opts) {
if (!zrUtil.isArray(graphs)) {
graphs = [graphs];
}
var graph0 = graphs[0];
var groups = [];
// Init groups
graph0.eachNode(function (node) {
var group = {
size: 0,
subGroups: [],
node: node
};
groups.push(group);
});
zrUtil.each(graphs, function (graph) {
graph.eachEdge(function (edge) {
var g1 = groups[edge.node1.dataIndex];
g1.size += edge.getValue('value') || 0;
g1.subGroups.push({
size: edge.getValue('value'),
edge: edge
});
});
});
var sumSize = zrUtil.reduce(groups, function (sumSize, group) {
return sumSize + group.size;
}, 0);
if (opts.sort && opts.sort != 'none') {
groups.sort(compareGroups);
if (opts.sort === 'descending') {
groups.revert();
}
}
var unitAngle = (Math.PI * 2 - opts.padding * graph0.data.count()) / sumSize;
var angle = opts.startAngle * Math.PI / 180;
var sign = opts.clockwise ? -1 : 1;
zrUtil.each(groups, function (group) {
if (opts.sortSub && opts.sortSub != 'none') {
group.subGroups.sort(compareGroups);
if (opts.sortSub === 'descending') {
group.subGroups.revert();
}
}
var endAngle = angle + sign * group.size * unitAngle;
group.node.setLayout({
startAngle: -angle,
endAngle: -endAngle,
cx: opts.cx,
cy: opts.cy,
r0: opts.r0,
r: opts.r,
clockwise: opts.clockwise
});
zrUtil.each(group.subGroups, function (subGroup) {
var startAngle = angle;
var endAngle = angle + sign * subGroup.size * unitAngle;
var layout = subGroup.edge.getLayout() || {
cx: opts.cx,
cy: opts.cy,
r: opts.r0,
clockwise: opts.clockwise
};
layout.startAngle = -startAngle;
layout.endAngle = -endAngle;
subGroup.edge.setLayout(layout);
angle = endAngle;
});
angle = endAngle + sign * opts.padding;
});
}
var compareGroups = function (a, b) {
return a.size - b.size;
};
return function (ecModel, api) {
ecModel.eachSeriesByType('chord', function (chordSeries) {
var graph = chordSeries.getGraph();
var center = chordSeries.get('center');
var radius = chordSeries.get('radius');
var parsePercent = numberUtil.parsePercent;
var viewWidth = api.getWidth();
var viewHeight = api.getHeight();
var viewSize = Math.min(viewWidth, viewHeight) / 2;
layout(graph, {
sort: chordSeries.get('sort'),
sortSub: chordSeries.get('sortSub'),
padding: chordSeries.get('padding'),
startAngle: chordSeries.get('startAngle'),
clockwise: chordSeries.get('clockwise'),
cx: parsePercent(center[0], viewWidth),
cy: parsePercent(center[1], viewHeight),
r0: parsePercent(radius[0], viewSize),
r: parsePercent(radius[1], viewSize)
});
});
};
});

View File

@@ -0,0 +1,15 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
require('./effectScatter/EffectScatterSeries');
require('./effectScatter/EffectScatterView');
echarts.registerVisualCoding('chart', zrUtil.curry(
require('../visual/symbol'), 'effectScatter', 'circle', null
));
echarts.registerLayout(zrUtil.curry(
require('../layout/points'), 'effectScatter'
));
});

View File

@@ -0,0 +1,64 @@
define(function (require) {
'use strict';
var createListFromArray = require('../helper/createListFromArray');
var SeriesModel = require('../../model/Series');
return SeriesModel.extend({
type: 'series.effectScatter',
dependencies: ['grid', 'polar'],
getInitialData: function (option, ecModel) {
var list = createListFromArray(option.data, this, ecModel);
return list;
},
defaultOption: {
coordinateSystem: 'cartesian2d',
zlevel: 0,
z: 2,
legendHoverLink: true,
effectType: 'ripple',
// When to show the effect, option: 'render'|'emphasis'
showEffectOn: 'render',
// Ripple effect config
rippleEffect: {
period: 4,
// Scale of ripple
scale: 2.5,
// Brush type can be fill or stroke
brushType: 'fill'
},
// Cartesian coordinate system
xAxisIndex: 0,
yAxisIndex: 0,
// Polar coordinate system
polarIndex: 0,
// Geo coordinate system
geoIndex: 0,
// symbol: null, // 图形类型
symbolSize: 10 // 图形大小半宽半径参数当图形为方向或菱形则总宽度为symbolSize * 2
// symbolRotate: null, // 图形旋转控制
// large: false,
// Available when large is true
// largeThreshold: 2000,
// itemStyle: {
// normal: {
// opacity: 1
// }
// }
}
});
});

View File

@@ -0,0 +1,29 @@
define(function (require) {
var SymbolDraw = require('../helper/SymbolDraw');
var EffectSymbol = require('../helper/EffectSymbol');
require('../../echarts').extendChartView({
type: 'effectScatter',
init: function () {
this._symbolDraw = new SymbolDraw(EffectSymbol);
},
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var effectSymbolDraw = this._symbolDraw;
effectSymbolDraw.updateData(data);
this.group.add(effectSymbolDraw.group);
},
updateLayout: function () {
this._symbolDraw.updateLayout();
},
remove: function (ecModel, api) {
this._symbolDraw && this._symbolDraw.remove(api);
}
});
});

17
vendors/echarts/src/chart/funnel.js vendored Normal file
View File

@@ -0,0 +1,17 @@
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')
);
});

View File

@@ -0,0 +1,101 @@
define(function(require) {
'use strict';
var List = require('../../data/List');
var modelUtil = require('../../util/model');
var completeDimensions = require('../../data/helper/completeDimensions');
var FunnelSeries = require('../../echarts').extendSeriesModel({
type: 'series.funnel',
init: function (option) {
FunnelSeries.superApply(this, 'init', 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;
};
// Extend labelLine emphasis
this._defaultLabelLine(option);
},
getInitialData: function (option, ecModel) {
var dimensions = completeDimensions(['value'], option.data);
var list = new List(dimensions, this);
list.initData(option.data);
return list;
},
_defaultLabelLine: function (option) {
// Extend labelLine emphasis
modelUtil.defaultEmphasis(option.labelLine, ['show']);
var labelLineNormalOpt = option.labelLine.normal;
var labelLineEmphasisOpt = option.labelLine.emphasis;
// Not show label line if `label.normal.show = false`
labelLineNormalOpt.show = labelLineNormalOpt.show
&& option.label.normal.show;
labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
&& option.label.emphasis.show;
},
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
legendHoverLink: true,
left: 80,
top: 60,
right: 80,
bottom: 60,
// width: {totalWidth} - left - right,
// height: {totalHeight} - top - bottom,
// 默认取数据最小最大值
// 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: {
normal: {
show: true,
length: 20,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
},
emphasis: {}
},
itemStyle: {
normal: {
// color: 各异,
borderColor: '#fff',
borderWidth: 1
},
emphasis: {
// color: 各异,
}
}
}
});
return FunnelSeries;
});

View File

@@ -0,0 +1,213 @@
define(function (require) {
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
/**
* Piece of pie including Sector, Label, LabelLine
* @constructor
* @extends {module:zrender/graphic/Group}
*/
function FunnelPiece(data, idx) {
graphic.Group.call(this);
var polygon = new graphic.Polygon();
var labelLine = new graphic.Polyline();
var text = new graphic.Text();
this.add(polygon);
this.add(labelLine);
this.add(text);
this.updateData(data, idx, true);
// Hover to change label and labelLine
function onEmphasis() {
labelLine.ignore = labelLine.hoverIgnore;
text.ignore = text.hoverIgnore;
}
function onNormal() {
labelLine.ignore = labelLine.normalIgnore;
text.ignore = text.normalIgnore;
}
this.on('emphasis', onEmphasis)
.on('normal', onNormal)
.on('mouseover', onEmphasis)
.on('mouseout', onNormal);
}
var funnelPieceProto = FunnelPiece.prototype;
function getLabelStyle(data, idx, state, labelModel) {
var textStyleModel = labelModel.getModel('textStyle');
var position = labelModel.get('position');
var isLabelInside = position === 'inside' || position === 'inner' || position === 'center';
return {
fill: textStyleModel.getTextColor()
|| (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')),
textFont: textStyleModel.getFont(),
text: zrUtil.retrieve(
data.hostModel.getFormattedLabel(idx, state),
data.getName(idx)
)
};
}
var opacityAccessPath = ['itemStyle', 'normal', 'opacity'];
funnelPieceProto.updateData = function (data, idx, firstCreate) {
var polygon = this.childAt(0);
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var opacity = data.getItemModel(idx).get(opacityAccessPath);
opacity = opacity == null ? 1 : opacity;
// Reset style
polygon.useStyle({});
if (firstCreate) {
polygon.setShape({
points: layout.points
});
polygon.setStyle({ opacity : 0 });
graphic.initProps(polygon, {
style: {
opacity: opacity
}
}, seriesModel, idx);
}
else {
graphic.updateProps(polygon, {
style: {
opacity: opacity
},
shape: {
points: layout.points
}
}, seriesModel, idx);
}
// Update common style
var itemStyleModel = itemModel.getModel('itemStyle');
var visualColor = data.getItemVisual(idx, 'color');
polygon.setStyle(
zrUtil.defaults(
{
fill: visualColor
},
itemStyleModel.getModel('normal').getItemStyle(['opacity'])
)
);
polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle();
this._updateLabel(data, idx);
graphic.setHoverStyle(this);
};
funnelPieceProto._updateLabel = function (data, idx) {
var labelLine = this.childAt(1);
var labelText = this.childAt(2);
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var labelLayout = layout.label;
var visualColor = data.getItemVisual(idx, 'color');
graphic.updateProps(labelLine, {
shape: {
points: labelLayout.linePoints || labelLayout.linePoints
}
}, seriesModel, idx);
graphic.updateProps(labelText, {
style: {
x: labelLayout.x,
y: labelLayout.y
}
}, seriesModel, idx);
labelText.attr({
style: {
textAlign: labelLayout.textAlign,
textVerticalAlign: labelLayout.verticalAlign,
textFont: labelLayout.font
},
rotation: labelLayout.rotation,
origin: [labelLayout.x, labelLayout.y],
z2: 10
});
var labelModel = itemModel.getModel('label.normal');
var labelHoverModel = itemModel.getModel('label.emphasis');
var labelLineModel = itemModel.getModel('labelLine.normal');
var labelLineHoverModel = itemModel.getModel('labelLine.emphasis');
labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel));
labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
labelText.hoverIgnore = !labelHoverModel.get('show');
labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
labelLine.hoverIgnore = !labelLineHoverModel.get('show');
// Default use item visual color
labelLine.setStyle({
stroke: visualColor
});
labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel);
labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
};
zrUtil.inherits(FunnelPiece, graphic.Group);
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;
data.diff(oldData)
.add(function (idx) {
var funnelPiece = new FunnelPiece(data, idx);
data.setItemGraphicEl(idx, funnelPiece);
group.add(funnelPiece);
})
.update(function (newIdx, oldIdx) {
var piePiece = oldData.getItemGraphicEl(oldIdx);
piePiece.updateData(data, newIdx);
group.add(piePiece);
data.setItemGraphicEl(newIdx, piePiece);
})
.remove(function (idx) {
var piePiece = oldData.getItemGraphicEl(idx);
group.remove(piePiece);
})
.execute();
this._data = data;
},
remove: function () {
this.group.removeAll();
this._data = null;
}
});
return Funnel;
});

View File

@@ -0,0 +1,170 @@
define(function (require) {
var layout = require('../../util/layout');
var number = require('../../util/number');
var parsePercent = number.parsePercent;
function getViewRect(seriesModel, api) {
return layout.getLayoutRect(
seriesModel.getBoxLayoutParams(), {
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.normal');
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;
textAlign = 'center';
linePoints = [
[textX, textY], [textX, textY]
];
}
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,
verticalAlign: 'middle',
textAlign: textAlign,
inside: isLabelInside
};
});
}
return function (ecModel, api) {
ecModel.eachSeriesByType('funnel', 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');
var min = seriesModel.get('min');
var max = seriesModel.get('max');
if (min == null) {
min = Math.min(dataExtent[0], 0);
}
if (max == null) {
max = dataExtent[1];
}
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) {
// End point index is data.count() and we assign it 0
var val = data.get('value', idx) || 0;
var itemWidth = number.linearMap(val, [min, max], sizeExtent, true);
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 = getLinePoints(nextIdx, y + itemHeight);
y += itemHeight + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
}
labelLayout(data);
});
};
});

4
vendors/echarts/src/chart/gauge.js vendored Normal file
View File

@@ -0,0 +1,4 @@
define(function (require) {
require('./gauge/GaugeSeries');
require('./gauge/GaugeView');
});

View File

@@ -0,0 +1,122 @@
define(function (require) {
var List = require('../../data/List');
var SeriesModel = require('../../model/Series');
var zrUtil = require('zrender/core/util');
var GaugeSeries = SeriesModel.extend({
type: 'series.gauge',
getInitialData: function (option, ecModel) {
var list = new List(['value'], this);
var dataOpt = option.data || [];
if (!zrUtil.isArray(dataOpt)) {
dataOpt = [dataOpt];
}
// Only use the first data item
list.initData(dataOpt);
return list;
},
defaultOption: {
zlevel: 0,
z: 2,
// 默认全局居中
center: ['50%', '50%'],
legendHoverLink: true,
radius: '75%',
startAngle: 225,
endAngle: -45,
clockwise: true,
// 最小值
min: 0,
// 最大值
max: 100,
// 分割段数默认为10
splitNumber: 10,
// 坐标轴线
axisLine: {
// 默认显示属性show控制显示与否
show: true,
lineStyle: { // 属性lineStyle控制线条样式
color: [[0.2, '#91c7ae'], [0.8, '#63869e'], [1, '#c23531']],
width: 30
}
},
// 分隔线
splitLine: {
// 默认显示属性show控制显示与否
show: true,
// 属性length控制线长
length: 30,
// 属性lineStyle详见lineStyle控制线条样式
lineStyle: {
color: '#eee',
width: 2,
type: 'solid'
}
},
// 坐标轴小标记
axisTick: {
// 属性show控制显示与否默认不显示
show: true,
// 每份split细分多少段
splitNumber: 5,
// 属性length控制线长
length: 8,
// 属性lineStyle控制线条样式
lineStyle: {
color: '#eee',
width: 1,
type: 'solid'
}
},
axisLabel: {
show: true,
// formatter: null,
textStyle: { // 其余属性默认使用全局文本样式详见TEXTSTYLE
color: 'auto'
}
},
pointer: {
show: true,
length: '80%',
width: 8
},
itemStyle: {
normal: {
color: 'auto'
}
},
title: {
show: true,
// x, y单位px
offsetCenter: [0, '-40%'],
// 其余属性默认使用全局文本样式详见TEXTSTYLE
textStyle: {
color: '#333',
fontSize: 15
}
},
detail: {
show: true,
backgroundColor: 'rgba(0,0,0,0)',
borderWidth: 0,
borderColor: '#ccc',
width: 100,
height: 40,
// x, y单位px
offsetCenter: [0, '40%'],
// formatter: null,
// 其余属性默认使用全局文本样式详见TEXTSTYLE
textStyle: {
color: 'auto',
fontSize: 30
}
}
}
});
return GaugeSeries;
});

View File

@@ -0,0 +1,410 @@
define(function (require) {
var PointerPath = require('./PointerPath');
var graphic = require('../../util/graphic');
var numberUtil = require('../../util/number');
var parsePercent = numberUtil.parsePercent;
function parsePosition(seriesModel, api) {
var center = seriesModel.get('center');
var width = api.getWidth();
var height = api.getHeight();
var size = Math.min(width, height);
var cx = parsePercent(center[0], api.getWidth());
var cy = parsePercent(center[1], api.getHeight());
var r = parsePercent(seriesModel.get('radius'), size / 2);
return {
cx: cx,
cy: cy,
r: r
};
}
function formatLabel(label, labelFormatter) {
if (labelFormatter) {
if (typeof labelFormatter === 'string') {
label = labelFormatter.replace('{value}', label);
}
else if (typeof labelFormatter === 'function') {
label = labelFormatter(label);
}
}
return label;
}
var PI2 = Math.PI * 2;
var GaugeView = require('../../view/Chart').extend({
type: 'gauge',
render: function (seriesModel, ecModel, api) {
this.group.removeAll();
var colorList = seriesModel.get('axisLine.lineStyle.color');
var posInfo = parsePosition(seriesModel, api);
this._renderMain(
seriesModel, ecModel, api, colorList, posInfo
);
},
_renderMain: function (seriesModel, ecModel, api, colorList, posInfo) {
var group = this.group;
var axisLineModel = seriesModel.getModel('axisLine');
var lineStyleModel = axisLineModel.getModel('lineStyle');
var clockwise = seriesModel.get('clockwise');
var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;
var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;
var angleRangeSpan = (endAngle - startAngle) % PI2;
var prevEndAngle = startAngle;
var axisLineWidth = lineStyleModel.get('width');
for (var i = 0; i < colorList.length; i++) {
// Clamp
var percent = Math.min(Math.max(colorList[i][0], 0), 1);
var endAngle = startAngle + angleRangeSpan * percent;
var sector = new graphic.Sector({
shape: {
startAngle: prevEndAngle,
endAngle: endAngle,
cx: posInfo.cx,
cy: posInfo.cy,
clockwise: clockwise,
r0: posInfo.r - axisLineWidth,
r: posInfo.r
},
silent: true
});
sector.setStyle({
fill: colorList[i][1]
});
sector.setStyle(lineStyleModel.getLineStyle(
// Because we use sector to simulate arc
// so the properties for stroking are useless
['color', 'borderWidth', 'borderColor']
));
group.add(sector);
prevEndAngle = endAngle;
}
var getColor = function (percent) {
// Less than 0
if (percent <= 0) {
return colorList[0][1];
}
for (var i = 0; i < colorList.length; i++) {
if (colorList[i][0] >= percent
&& (i === 0 ? 0 : colorList[i - 1][0]) < percent
) {
return colorList[i][1];
}
}
// More than 1
return colorList[i - 1][1];
};
if (!clockwise) {
var tmp = startAngle;
startAngle = endAngle;
endAngle = tmp;
}
this._renderTicks(
seriesModel, ecModel, api, getColor, posInfo,
startAngle, endAngle, clockwise
);
this._renderPointer(
seriesModel, ecModel, api, getColor, posInfo,
startAngle, endAngle, clockwise
);
this._renderTitle(
seriesModel, ecModel, api, getColor, posInfo
);
this._renderDetail(
seriesModel, ecModel, api, getColor, posInfo
);
},
_renderTicks: function (
seriesModel, ecModel, api, getColor, posInfo,
startAngle, endAngle, clockwise
) {
var group = this.group;
var cx = posInfo.cx;
var cy = posInfo.cy;
var r = posInfo.r;
var minVal = seriesModel.get('min');
var maxVal = seriesModel.get('max');
var splitLineModel = seriesModel.getModel('splitLine');
var tickModel = seriesModel.getModel('axisTick');
var labelModel = seriesModel.getModel('axisLabel');
var splitNumber = seriesModel.get('splitNumber');
var subSplitNumber = tickModel.get('splitNumber');
var splitLineLen = parsePercent(
splitLineModel.get('length'), r
);
var tickLen = parsePercent(
tickModel.get('length'), r
);
var angle = startAngle;
var step = (endAngle - startAngle) / splitNumber;
var subStep = step / subSplitNumber;
var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();
var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();
var textStyleModel = labelModel.getModel('textStyle');
for (var i = 0; i <= splitNumber; i++) {
var unitX = Math.cos(angle);
var unitY = Math.sin(angle);
// Split line
if (splitLineModel.get('show')) {
var splitLine = new graphic.Line({
shape: {
x1: unitX * r + cx,
y1: unitY * r + cy,
x2: unitX * (r - splitLineLen) + cx,
y2: unitY * (r - splitLineLen) + cy
},
style: splitLineStyle,
silent: true
});
if (splitLineStyle.stroke === 'auto') {
splitLine.setStyle({
stroke: getColor(i / splitNumber)
});
}
group.add(splitLine);
}
// Label
if (labelModel.get('show')) {
var label = formatLabel(
numberUtil.round(i / splitNumber * (maxVal - minVal) + minVal),
labelModel.get('formatter')
);
var text = new graphic.Text({
style: {
text: label,
x: unitX * (r - splitLineLen - 5) + cx,
y: unitY * (r - splitLineLen - 5) + cy,
fill: textStyleModel.getTextColor(),
textFont: textStyleModel.getFont(),
textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'),
textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center')
},
silent: true
});
if (text.style.fill === 'auto') {
text.setStyle({
fill: getColor(i / splitNumber)
});
}
group.add(text);
}
// Axis tick
if (tickModel.get('show') && i !== splitNumber) {
for (var j = 0; j <= subSplitNumber; j++) {
var unitX = Math.cos(angle);
var unitY = Math.sin(angle);
var tickLine = new graphic.Line({
shape: {
x1: unitX * r + cx,
y1: unitY * r + cy,
x2: unitX * (r - tickLen) + cx,
y2: unitY * (r - tickLen) + cy
},
silent: true,
style: tickLineStyle
});
if (tickLineStyle.stroke === 'auto') {
tickLine.setStyle({
stroke: getColor((i + j / subSplitNumber) / splitNumber)
});
}
group.add(tickLine);
angle += subStep;
}
angle -= subStep;
}
else {
angle += step;
}
}
},
_renderPointer: function (
seriesModel, ecModel, api, getColor, posInfo,
startAngle, endAngle, clockwise
) {
var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')];
var angleExtent = [startAngle, endAngle];
if (!clockwise) {
angleExtent = angleExtent.reverse();
}
var data = seriesModel.getData();
var oldData = this._data;
var group = this.group;
data.diff(oldData)
.add(function (idx) {
var pointer = new PointerPath({
shape: {
angle: startAngle
}
});
graphic.updateProps(pointer, {
shape: {
angle: numberUtil.linearMap(data.get('value', idx), valueExtent, angleExtent, true)
}
}, seriesModel);
group.add(pointer);
data.setItemGraphicEl(idx, pointer);
})
.update(function (newIdx, oldIdx) {
var pointer = oldData.getItemGraphicEl(oldIdx);
graphic.updateProps(pointer, {
shape: {
angle: numberUtil.linearMap(data.get('value', newIdx), valueExtent, angleExtent, true)
}
}, seriesModel);
group.add(pointer);
data.setItemGraphicEl(newIdx, pointer);
})
.remove(function (idx) {
var pointer = oldData.getItemGraphicEl(idx);
group.remove(pointer);
})
.execute();
data.eachItemGraphicEl(function (pointer, idx) {
var itemModel = data.getItemModel(idx);
var pointerModel = itemModel.getModel('pointer');
pointer.setShape({
x: posInfo.cx,
y: posInfo.cy,
width: parsePercent(
pointerModel.get('width'), posInfo.r
),
r: parsePercent(pointerModel.get('length'), posInfo.r)
});
pointer.useStyle(itemModel.getModel('itemStyle.normal').getItemStyle());
if (pointer.style.fill === 'auto') {
pointer.setStyle('fill', getColor(
(data.get('value', idx) - valueExtent[0]) / (valueExtent[1] - valueExtent[0])
));
}
graphic.setHoverStyle(
pointer, itemModel.getModel('itemStyle.emphasis').getItemStyle()
);
});
this._data = data;
},
_renderTitle: function (
seriesModel, ecModel, api, getColor, posInfo
) {
var titleModel = seriesModel.getModel('title');
if (titleModel.get('show')) {
var textStyleModel = titleModel.getModel('textStyle');
var offsetCenter = titleModel.get('offsetCenter');
var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
var text = new graphic.Text({
style: {
x: x,
y: y,
// FIXME First data name ?
text: seriesModel.getData().getName(0),
fill: textStyleModel.getTextColor(),
textFont: textStyleModel.getFont(),
textAlign: 'center',
textVerticalAlign: 'middle'
}
});
this.group.add(text);
}
},
_renderDetail: function (
seriesModel, ecModel, api, getColor, posInfo
) {
var detailModel = seriesModel.getModel('detail');
var minVal = seriesModel.get('min');
var maxVal = seriesModel.get('max');
if (detailModel.get('show')) {
var textStyleModel = detailModel.getModel('textStyle');
var offsetCenter = detailModel.get('offsetCenter');
var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
var width = parsePercent(detailModel.get('width'), posInfo.r);
var height = parsePercent(detailModel.get('height'), posInfo.r);
var value = seriesModel.getData().get('value', 0);
var rect = new graphic.Rect({
shape: {
x: x - width / 2,
y: y - height / 2,
width: width,
height: height
},
style: {
text: formatLabel(
// FIXME First data name ?
value, detailModel.get('formatter')
),
fill: detailModel.get('backgroundColor'),
textFill: textStyleModel.getTextColor(),
textFont: textStyleModel.getFont()
}
});
if (rect.style.textFill === 'auto') {
rect.setStyle('textFill', getColor(
numberUtil.linearMap(value, [minVal, maxVal], [0, 1], true)
));
}
rect.setStyle(detailModel.getItemStyle(['color']));
this.group.add(rect);
}
}
});
return GaugeView;
});

View File

@@ -0,0 +1,47 @@
define(function (require) {
return require('zrender/graphic/Path').extend({
type: 'echartsGaugePointer',
shape: {
angle: 0,
width: 10,
r: 10,
x: 0,
y: 0
},
buildPath: function (ctx, shape) {
var mathCos = Math.cos;
var mathSin = Math.sin;
var r = shape.r;
var width = shape.width;
var angle = shape.angle;
var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2);
var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2);
angle = shape.angle - Math.PI / 2;
ctx.moveTo(x, y);
ctx.lineTo(
shape.x + mathCos(angle) * width,
shape.y + mathSin(angle) * width
);
ctx.lineTo(
shape.x + mathCos(shape.angle) * r,
shape.y + mathSin(shape.angle) * r
);
ctx.lineTo(
shape.x - mathCos(angle) * width,
shape.y - mathSin(angle) * width
);
ctx.lineTo(x, y);
return;
}
});
});

27
vendors/echarts/src/chart/graph.js vendored Normal file
View File

@@ -0,0 +1,27 @@
define(function (require) {
var echarts = require('../echarts');
var zrUtil = require('zrender/core/util');
require('./graph/GraphSeries');
require('./graph/GraphView');
require('./graph/roamAction');
echarts.registerProcessor('filter', require('./graph/categoryFilter'));
echarts.registerVisualCoding('chart', zrUtil.curry(
require('../visual/symbol'), 'graph', 'circle', null
));
echarts.registerVisualCoding('chart', require('./graph/categoryVisual'));
echarts.registerVisualCoding('chart', require('./graph/edgeVisual'));
echarts.registerLayout(require('./graph/simpleLayout'));
echarts.registerLayout(require('./graph/circularLayout'));
echarts.registerLayout(require('./graph/forceLayout'));
// Graph view coordinate system
echarts.registerCoordinateSystem('graphView', {
create: require('./graph/createView')
});
});

View File

@@ -0,0 +1,249 @@
define(function (require) {
'use strict';
var List = require('../../data/List');
var zrUtil = require('zrender/core/util');
var modelUtil = require('../../util/model');
var Model = require('../../model/Model');
var createGraphFromNodeEdge = require('../helper/createGraphFromNodeEdge');
var GraphSeries = require('../../echarts').extendSeriesModel({
type: 'series.graph',
init: function (option) {
GraphSeries.superApply(this, 'init', arguments);
// Provide data for legend select
this.legendDataProvider = function () {
return this._categoriesData;
};
this.fillDataTextStyle(option.edges || option.links);
this._updateCategoriesData();
},
mergeOption: function (option) {
GraphSeries.superApply(this, 'mergeOption', arguments);
this.fillDataTextStyle(option.edges || option.links);
this._updateCategoriesData();
},
mergeDefaultAndTheme: function (option) {
GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments);
modelUtil.defaultEmphasis(option.edgeLabel, modelUtil.LABEL_OPTIONS);
},
getInitialData: function (option, ecModel) {
var edges = option.edges || option.links || [];
var nodes = option.data || option.nodes || [];
var self = this;
if (nodes && edges) {
return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data;
}
function beforeLink(nodeData, edgeData) {
// Overwrite nodeData.getItemModel to
nodeData.wrapMethod('getItemModel', function (model) {
var categoriesModels = self._categoriesModels;
var categoryIdx = model.getShallow('category');
var categoryModel = categoriesModels[categoryIdx];
if (categoryModel) {
categoryModel.parentModel = model.parentModel;
model.parentModel = categoryModel;
}
return model;
});
var edgeLabelModel = self.getModel('edgeLabel');
var wrappedGetEdgeModel = function (path, parentModel) {
var pathArr = (path || '').split('.');
if (pathArr[0] === 'label') {
parentModel = parentModel
|| edgeLabelModel.getModel(pathArr.slice(1));
}
var model = Model.prototype.getModel.call(this, pathArr, parentModel);
model.getModel = wrappedGetEdgeModel;
return model;
};
edgeData.wrapMethod('getItemModel', function (model) {
// FIXME Wrap get method ?
model.getModel = wrappedGetEdgeModel;
return model;
});
}
},
/**
* @return {module:echarts/data/Graph}
*/
getGraph: function () {
return this.getData().graph;
},
/**
* @return {module:echarts/data/List}
*/
getEdgeData: function () {
return this.getGraph().edgeData;
},
/**
* @return {module:echarts/data/List}
*/
getCategoriesData: function () {
return this._categoriesData;
},
/**
* @override
*/
formatTooltip: function (dataIndex, multipleSeries, dataType) {
if (dataType === 'edge') {
var nodeData = this.getData();
var params = this.getDataParams(dataIndex, dataType);
var edge = nodeData.graph.getEdgeByIndex(dataIndex);
var sourceName = nodeData.getName(edge.node1.dataIndex);
var targetName = nodeData.getName(edge.node2.dataIndex);
var html = sourceName + ' > ' + targetName;
if (params.value) {
html += ' : ' + params.value;
}
return html;
}
else { // dataType === 'node' or empty
return GraphSeries.superApply(this, 'formatTooltip', arguments);
}
},
_updateCategoriesData: function () {
var categories = zrUtil.map(this.option.categories || [], function (category) {
// Data must has value
return category.value != null ? category : zrUtil.extend({
value: 0
}, category);
});
var categoriesData = new List(['value'], this);
categoriesData.initData(categories);
this._categoriesData = categoriesData;
this._categoriesModels = categoriesData.mapArray(function (idx) {
return categoriesData.getItemModel(idx, true);
});
},
setZoom: function (zoom) {
this.option.zoom = zoom;
},
setCenter: function (center) {
this.option.center = center;
},
defaultOption: {
zlevel: 0,
z: 2,
color: ['#61a0a8', '#d14a61', '#fd9c35', '#675bba', '#fec42c',
'#dd4444', '#fd9c35', '#cd4870'],
coordinateSystem: 'view',
// Default option for all coordinate systems
xAxisIndex: 0,
yAxisIndex: 0,
polarIndex: 0,
geoIndex: 0,
legendHoverLink: true,
hoverAnimation: true,
layout: null,
// Configuration of force
force: {
initLayout: null,
repulsion: 50,
gravity: 0.1,
edgeLength: 30,
layoutAnimation: true
},
left: 'center',
top: 'center',
// right: null,
// bottom: null,
// width: '80%',
// height: '80%',
symbol: 'circle',
symbolSize: 10,
edgeSymbol: ['none', 'none'],
edgeSymbolSize: 10,
edgeLabel: {
normal: {
position: 'middle'
},
emphasis: {}
},
draggable: false,
roam: false,
// Default on center of graph
center: null,
zoom: 1,
// Symbol size scale ratio in roam
nodeScaleRatio: 0.6,
// categories: [],
// data: []
// Or
// nodes: []
//
// links: []
// Or
// edges: []
label: {
normal: {
show: false,
formatter: '{b}'
},
emphasis: {
show: true
}
},
itemStyle: {
normal: {},
emphasis: {}
},
lineStyle: {
normal: {
color: '#aaa',
width: 1,
curveness: 0,
opacity: 0.5
},
emphasis: {}
}
}
});
return GraphSeries;
});

View File

@@ -0,0 +1,204 @@
define(function (require) {
var SymbolDraw = require('../helper/SymbolDraw');
var LineDraw = require('../helper/LineDraw');
var RoamController = require('../../component/helper/RoamController');
var graphic = require('../../util/graphic');
var adjustEdge = require('./adjustEdge');
require('../../echarts').extendChartView({
type: 'graph',
init: function (ecModel, api) {
var symbolDraw = new SymbolDraw();
var lineDraw = new LineDraw();
var group = this.group;
var controller = new RoamController(api.getZr(), group);
group.add(symbolDraw.group);
group.add(lineDraw.group);
this._symbolDraw = symbolDraw;
this._lineDraw = lineDraw;
this._controller = controller;
this._firstRender = true;
},
render: function (seriesModel, ecModel, api) {
var coordSys = seriesModel.coordinateSystem;
// Only support view and geo coordinate system
// if (coordSys.type !== 'geo' && coordSys.type !== 'view') {
// return;
// }
this._model = seriesModel;
this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');
var symbolDraw = this._symbolDraw;
var lineDraw = this._lineDraw;
var group = this.group;
if (coordSys.type === 'view') {
var groupNewProp = {
position: coordSys.position,
scale: coordSys.scale
};
if (this._firstRender) {
group.attr(groupNewProp);
}
else {
graphic.updateProps(group, groupNewProp, seriesModel);
}
}
// Fix edge contact point with node
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
var data = seriesModel.getData();
symbolDraw.updateData(data);
var edgeData = seriesModel.getEdgeData();
lineDraw.updateData(edgeData);
this._updateNodeAndLinkScale();
this._updateController(seriesModel, api);
clearTimeout(this._layoutTimeout);
var forceLayout = seriesModel.forceLayout;
var layoutAnimation = seriesModel.get('force.layoutAnimation');
if (forceLayout) {
this._startForceLayoutIteration(forceLayout, layoutAnimation);
}
// Update draggable
data.eachItemGraphicEl(function (el, idx) {
var draggable = data.getItemModel(idx).get('draggable');
if (draggable) {
el.on('drag', function () {
if (forceLayout) {
forceLayout.warmUp();
!this._layouting
&& this._startForceLayoutIteration(forceLayout, layoutAnimation);
forceLayout.setFixed(idx);
// Write position back to layout
data.setItemLayout(idx, el.position);
}
}, this).on('dragend', function () {
if (forceLayout) {
forceLayout.setUnfixed(idx);
}
}, this);
}
else {
el.off('drag');
}
el.setDraggable(draggable && forceLayout);
}, this);
this._firstRender = false;
},
_startForceLayoutIteration: function (forceLayout, layoutAnimation) {
var self = this;
(function step() {
forceLayout.step(function (stopped) {
self.updateLayout(self._model);
(self._layouting = !stopped) && (
layoutAnimation
? (self._layoutTimeout = setTimeout(step, 16))
: step()
);
});
})();
},
_updateController: function (seriesModel, api) {
var controller = this._controller;
var group = this.group;
controller.rectProvider = function () {
var rect = group.getBoundingRect();
rect.applyTransform(group.transform);
return rect;
};
if (seriesModel.coordinateSystem.type !== 'view') {
controller.disable();
return;
}
controller.enable(seriesModel.get('roam'));
controller.zoomLimit = seriesModel.get('scaleLimit');
// Update zoom from model
controller.zoom = seriesModel.coordinateSystem.getZoom();
controller
.off('pan')
.off('zoom')
.on('pan', function (dx, dy) {
api.dispatchAction({
seriesId: seriesModel.id,
type: 'graphRoam',
dx: dx,
dy: dy
});
})
.on('zoom', function (zoom, mouseX, mouseY) {
api.dispatchAction({
seriesId: seriesModel.id,
type: 'graphRoam',
zoom: zoom,
originX: mouseX,
originY: mouseY
});
this._updateNodeAndLinkScale();
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
this._lineDraw.updateLayout();
}, this);
},
_updateNodeAndLinkScale: function () {
var seriesModel = this._model;
var data = seriesModel.getData();
var nodeScale = this._getNodeGlobalScale(seriesModel);
var invScale = [nodeScale, nodeScale];
data.eachItemGraphicEl(function (el, idx) {
el.attr('scale', invScale);
});
},
_getNodeGlobalScale: function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys.type !== 'view') {
return 1;
}
var nodeScaleRatio = this._nodeScaleRatio;
var groupScale = this.group.scale;
var groupZoom = (groupScale && groupScale[0]) || 1;
// Scale node when zoom changes
var roamZoom = coordSys.getZoom();
var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
return nodeScale / groupZoom;
},
updateLayout: function (seriesModel) {
this._symbolDraw.updateLayout();
this._lineDraw.updateLayout();
adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
},
remove: function (ecModel, api) {
this._symbolDraw && this._symbolDraw.remove();
this._lineDraw && this._lineDraw.remove();
}
});
});

View File

@@ -0,0 +1,145 @@
define(function (require) {
var curveTool = require('zrender/core/curve');
var vec2 = require('zrender/core/vector');
var v1 = [];
var v2 = [];
var v3 = [];
var quadraticAt = curveTool.quadraticAt;
var v2DistSquare = vec2.distSquare;
var mathAbs = Math.abs;
function intersectCurveCircle(curvePoints, center, radius) {
var p0 = curvePoints[0];
var p1 = curvePoints[1];
var p2 = curvePoints[2];
var d = Infinity;
var t;
var radiusSquare = radius * radius;
var interval = 0.1;
for (var _t = 0.1; _t <= 0.9; _t += 0.1) {
v1[0] = quadraticAt(p0[0], p1[0], p2[0], _t);
v1[1] = quadraticAt(p0[1], p1[1], p2[1], _t);
var diff = mathAbs(v2DistSquare(v1, center) - radiusSquare);
if (diff < d) {
d = diff;
t = _t;
}
}
// Assume the segment is monotoneFind root through Bisection method
// At most 32 iteration
for (var i = 0; i < 32; i++) {
// var prev = t - interval;
var next = t + interval;
// v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev);
// v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev);
v2[0] = quadraticAt(p0[0], p1[0], p2[0], t);
v2[1] = quadraticAt(p0[1], p1[1], p2[1], t);
v3[0] = quadraticAt(p0[0], p1[0], p2[0], next);
v3[1] = quadraticAt(p0[1], p1[1], p2[1], next);
var diff = v2DistSquare(v2, center) - radiusSquare;
if (mathAbs(diff) < 1e-2) {
break;
}
// var prevDiff = v2DistSquare(v1, center) - radiusSquare;
var nextDiff = v2DistSquare(v3, center) - radiusSquare;
interval /= 2;
if (diff < 0) {
if (nextDiff >= 0) {
t = t + interval;
}
else {
t = t - interval;
}
}
else {
if (nextDiff >= 0) {
t = t - interval;
}
else {
t = t + interval;
}
}
}
return t;
}
// Adjust edge to avoid
return function (graph, scale) {
var tmp0 = [];
var quadraticSubdivide = curveTool.quadraticSubdivide;
var pts = [[], [], []];
var pts2 = [[], []];
var v = [];
scale /= 2;
graph.eachEdge(function (edge) {
var linePoints = edge.getLayout();
var fromSymbol = edge.getVisual('fromSymbol');
var toSymbol = edge.getVisual('toSymbol');
if (!linePoints.__original) {
linePoints.__original = [
vec2.clone(linePoints[0]),
vec2.clone(linePoints[1])
];
if (linePoints[2]) {
linePoints.__original.push(vec2.clone(linePoints[2]));
}
}
var originalPoints = linePoints.__original;
// Quadratic curve
if (linePoints[2] != null) {
vec2.copy(pts[0], originalPoints[0]);
vec2.copy(pts[1], originalPoints[2]);
vec2.copy(pts[2], originalPoints[1]);
if (fromSymbol && fromSymbol != 'none') {
var t = intersectCurveCircle(pts, originalPoints[0], edge.node1.getVisual('symbolSize') * scale);
// Subdivide and get the second
quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0);
pts[0][0] = tmp0[3];
pts[1][0] = tmp0[4];
quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0);
pts[0][1] = tmp0[3];
pts[1][1] = tmp0[4];
}
if (toSymbol && toSymbol != 'none') {
var t = intersectCurveCircle(pts, originalPoints[1], edge.node2.getVisual('symbolSize') * scale);
// Subdivide and get the first
quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0);
pts[1][0] = tmp0[1];
pts[2][0] = tmp0[2];
quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0);
pts[1][1] = tmp0[1];
pts[2][1] = tmp0[2];
}
// Copy back to layout
vec2.copy(linePoints[0], pts[0]);
vec2.copy(linePoints[1], pts[2]);
vec2.copy(linePoints[2], pts[1]);
}
// Line
else {
vec2.copy(pts2[0], originalPoints[0]);
vec2.copy(pts2[1], originalPoints[1]);
vec2.sub(v, pts2[1], pts2[0]);
vec2.normalize(v, v);
if (fromSymbol && fromSymbol != 'none') {
vec2.scaleAndAdd(pts2[0], pts2[0], v, edge.node1.getVisual('symbolSize') * scale);
}
if (toSymbol && toSymbol != 'none') {
vec2.scaleAndAdd(pts2[1], pts2[1], v, -edge.node2.getVisual('symbolSize') * scale);
}
vec2.copy(linePoints[0], pts2[0]);
vec2.copy(linePoints[1], pts2[1]);
}
});
};
});

View File

@@ -0,0 +1,3 @@
define(function (require) {
});

View File

@@ -0,0 +1,35 @@
define(function (require) {
return function (ecModel) {
var legendModels = ecModel.findComponents({
mainType: 'legend'
});
if (!legendModels || !legendModels.length) {
return;
}
ecModel.eachSeriesByType('graph', function (graphSeries) {
var categoriesData = graphSeries.getCategoriesData();
var graph = graphSeries.getGraph();
var data = graph.data;
var categoryNames = categoriesData.mapArray(categoriesData.getName);
data.filterSelf(function (idx) {
var model = data.getItemModel(idx);
var category = model.getShallow('category');
if (category != null) {
if (typeof category === 'number') {
category = categoryNames[category];
}
// If in any legend component the status is not selected.
for (var i = 0; i < legendModels.length; i++) {
if (!legendModels[i].isSelected(category)) {
return false;
}
}
}
return true;
});
}, this);
};
});

View File

@@ -0,0 +1,41 @@
define(function (require) {
return function (ecModel) {
ecModel.eachSeriesByType('graph', function (seriesModel) {
var colorList = seriesModel.get('color');
var categoriesData = seriesModel.getCategoriesData();
var data = seriesModel.getData();
var categoryNameIdxMap = {};
categoriesData.each(function (idx) {
categoryNameIdxMap[categoriesData.getName(idx)] = idx;
var itemModel = categoriesData.getItemModel(idx);
var rawIdx = categoriesData.getRawIndex(idx);
var color = itemModel.get('itemStyle.normal.color')
|| colorList[rawIdx % colorList.length];
categoriesData.setItemVisual(idx, 'color', color);
});
// Assign category color to visual
if (categoriesData.count()) {
data.each(function (idx) {
var model = data.getItemModel(idx);
var category = model.getShallow('category');
if (category != null) {
if (typeof category === 'string') {
category = categoryNameIdxMap[category];
}
if (!data.getItemVisual(idx, 'color', true)) {
data.setItemVisual(
idx, 'color',
categoriesData.getItemVisual(category, 'color')
);
}
}
});
}
});
};
});

View File

@@ -0,0 +1,10 @@
define(function (require) {
var circularLayoutHelper = require('./circularLayoutHelper');
return function (ecModel, api) {
ecModel.eachSeriesByType('graph', function (seriesModel) {
if (seriesModel.get('layout') === 'circular') {
circularLayoutHelper(seriesModel);
}
});
};
});

View File

@@ -0,0 +1,53 @@
define(function (require) {
var vec2 = require('zrender/core/vector');
return function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.type !== 'view') {
return;
}
var rect = coordSys.getBoundingRect();
var nodeData = seriesModel.getData();
var graph = nodeData.graph;
var angle = 0;
var sum = nodeData.getSum('value');
var unitAngle = Math.PI * 2 / (sum || nodeData.count());
var cx = rect.width / 2 + rect.x;
var cy = rect.height / 2 + rect.y;
var r = Math.min(rect.width, rect.height) / 2;
graph.eachNode(function (node) {
var value = node.getValue('value');
angle += unitAngle * (sum ? value : 2) / 2;
node.setLayout([
r * Math.cos(angle) + cx,
r * Math.sin(angle) + cy
]);
angle += unitAngle * (sum ? value : 2) / 2;
});
graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0;
var p1 = vec2.clone(edge.node1.getLayout());
var p2 = vec2.clone(edge.node2.getLayout());
var cp1;
var x12 = (p1[0] + p2[0]) / 2;
var y12 = (p1[1] + p2[1]) / 2;
if (curveness > 0) {
curveness *= 3;
cp1 = [
cx * curveness + x12 * (1 - curveness),
cy * curveness + y12 * (1 - curveness)
];
}
edge.setLayout([p1, p2, cp1]);
});
};
});

View File

@@ -0,0 +1,76 @@
define(function (require) {
// FIXME Where to create the simple view coordinate system
var View = require('../../coord/View');
var layout = require('../../util/layout');
var bbox = require('zrender/core/bbox');
function getViewRect(seriesModel, api, aspect) {
var option = seriesModel.getBoxLayoutParams();
option.aspect = aspect;
return layout.getLayoutRect(option, {
width: api.getWidth(),
height: api.getHeight()
});
}
return function (ecModel, api) {
var viewList = [];
ecModel.eachSeriesByType('graph', function (seriesModel) {
var coordSysType = seriesModel.get('coordinateSystem');
if (!coordSysType || coordSysType === 'view') {
var viewCoordSys = new View();
viewList.push(viewCoordSys);
var data = seriesModel.getData();
var positions = data.mapArray(function (idx) {
var itemModel = data.getItemModel(idx);
return [+itemModel.get('x'), +itemModel.get('y')];
});
var min = [];
var max = [];
bbox.fromPoints(positions, min, max);
// If width or height is 0
if (max[0] - min[0] === 0) {
max[0] += 1;
min[0] -= 1;
}
if (max[1] - min[1] === 0) {
max[1] += 1;
min[1] -= 1;
}
var aspect = (max[0] - min[0]) / (max[1] - min[1]);
// FIXME If get view rect after data processed?
var viewRect = getViewRect(seriesModel, api, aspect);
// Position may be NaN, use view rect instead
if (isNaN(aspect)) {
min = [viewRect.x, viewRect.y];
max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height];
}
var bbWidth = max[0] - min[0];
var bbHeight = max[1] - min[1];
var viewWidth = viewRect.width;
var viewHeight = viewRect.height;
viewCoordSys = seriesModel.coordinateSystem = new View();
viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');
viewCoordSys.setBoundingRect(
min[0], min[1], bbWidth, bbHeight
);
viewCoordSys.setViewRect(
viewRect.x, viewRect.y, viewWidth, viewHeight
);
// Update roam info
viewCoordSys.setCenter(seriesModel.get('center'));
viewCoordSys.setZoom(seriesModel.get('zoom'));
}
});
return viewList;
};
});

View File

@@ -0,0 +1,33 @@
define(function (require) {
function normalize(a) {
if (!(a instanceof Array)) {
a = [a, a];
}
return a;
}
return function (ecModel) {
ecModel.eachSeriesByType('graph', function (seriesModel) {
var edgeData = seriesModel.getEdgeData();
var symbolType = normalize(seriesModel.get('edgeSymbol'));
var symbolSize = normalize(seriesModel.get('edgeSymbolSize'));
edgeData.setVisual('fromSymbol', symbolType && symbolType[0]);
edgeData.setVisual('toSymbol', symbolType && symbolType[1]);
edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]);
edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]);
edgeData.setVisual('color', seriesModel.get('lineStyle.normal.color'));
edgeData.each(function (idx) {
var itemModel = edgeData.getItemModel(idx);
var symbolType = normalize(itemModel.getShallow('symbol', true));
var symbolSize = normalize(itemModel.getShallow('symbolSize', true));
symbolType[0] && edgeData.setItemVisual(idx, 'fromSymbol', symbolType[0]);
symbolType[1] && edgeData.setItemVisual(idx, 'toSymbol', symbolType[1]);
symbolSize[0] && edgeData.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]);
symbolSize[1] && edgeData.setItemVisual(idx, 'toSymbolSize', symbolSize[1]);
});
});
};
});

View File

@@ -0,0 +1,137 @@
define(function (require) {
var vec2 = require('zrender/core/vector');
var scaleAndAdd = vec2.scaleAndAdd;
// function adjacentNode(n, e) {
// return e.n1 === n ? e.n2 : e.n1;
// }
return function (nodes, edges, opts) {
var rect = opts.rect;
var width = rect.width;
var height = rect.height;
var center = [rect.x + width / 2, rect.y + height / 2];
// var scale = opts.scale || 1;
var gravity = opts.gravity == null ? 0.1 : opts.gravity;
// for (var i = 0; i < edges.length; i++) {
// var e = edges[i];
// var n1 = e.n1;
// var n2 = e.n2;
// n1.edges = n1.edges || [];
// n2.edges = n2.edges || [];
// n1.edges.push(e);
// n2.edges.push(e);
// }
// Init position
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (!n.p) {
// Use the position from first adjecent node with defined position
// Or use a random position
// From d3
// if (n.edges) {
// var j = -1;
// while (++j < n.edges.length) {
// var e = n.edges[j];
// var other = adjacentNode(n, e);
// if (other.p) {
// n.p = vec2.clone(other.p);
// break;
// }
// }
// }
// if (!n.p) {
n.p = vec2.create(
width * (Math.random() - 0.5) + center[0],
height * (Math.random() - 0.5) + center[1]
);
// }
}
n.pp = vec2.clone(n.p);
n.edges = null;
}
// Formula in 'Graph Drawing by Force-directed Placement'
// var k = scale * Math.sqrt(width * height / nodes.length);
// var k2 = k * k;
var friction = 0.6;
return {
warmUp: function () {
friction = 0.5;
},
setFixed: function (idx) {
nodes[idx].fixed = true;
},
setUnfixed: function (idx) {
nodes[idx].fixed = false;
},
step: function (cb) {
var v12 = [];
var nLen = nodes.length;
for (var i = 0; i < edges.length; i++) {
var e = edges[i];
var n1 = e.n1;
var n2 = e.n2;
vec2.sub(v12, n2.p, n1.p);
var d = vec2.len(v12) - e.d;
var w = n2.w / (n1.w + n2.w);
vec2.normalize(v12, v12);
!n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction);
!n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction);
}
// Gravity
for (var i = 0; i < nLen; i++) {
var n = nodes[i];
if (!n.fixed) {
vec2.sub(v12, center, n.p);
// var d = vec2.len(v12);
// vec2.scale(v12, v12, 1 / d);
// var gravityFactor = gravity;
vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction);
}
}
// Repulsive
// PENDING
for (var i = 0; i < nLen; i++) {
var n1 = nodes[i];
for (var j = i + 1; j < nLen; j++) {
var n2 = nodes[j];
vec2.sub(v12, n2.p, n1.p);
var d = vec2.len(v12);
if (d === 0) {
// Random repulse
vec2.set(v12, Math.random() - 0.5, Math.random() - 0.5);
d = 1;
}
var repFact = (n1.rep + n2.rep) / d / d;
!n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact);
!n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact);
}
}
var v = [];
for (var i = 0; i < nLen; i++) {
var n = nodes[i];
if (!n.fixed) {
vec2.sub(v, n.p, n.pp);
vec2.scaleAndAdd(n.p, n.p, v, friction);
vec2.copy(n.pp, n.p);
}
}
friction = friction * 0.992;
cb && cb(nodes, edges, friction < 0.01);
}
};
};
});

View File

@@ -0,0 +1,111 @@
define(function (require) {
var forceHelper = require('./forceHelper');
var numberUtil = require('../../util/number');
var simpleLayoutHelper = require('./simpleLayoutHelper');
var circularLayoutHelper = require('./circularLayoutHelper');
var vec2 = require('zrender/core/vector');
return function (ecModel, api) {
ecModel.eachSeriesByType('graph', function (graphSeries) {
var coordSys = graphSeries.coordinateSystem;
if (coordSys && coordSys.type !== 'view') {
return;
}
if (graphSeries.get('layout') === 'force') {
var preservedPoints = graphSeries.preservedPoints || {};
var graph = graphSeries.getGraph();
var nodeData = graph.data;
var edgeData = graph.edgeData;
var forceModel = graphSeries.getModel('force');
var initLayout = forceModel.get('initLayout');
if (graphSeries.preservedPoints) {
nodeData.each(function (idx) {
var id = nodeData.getId(idx);
nodeData.setItemLayout(idx, preservedPoints[id] || [NaN, NaN]);
});
}
else if (!initLayout || initLayout === 'none') {
simpleLayoutHelper(graphSeries);
}
else if (initLayout === 'circular') {
circularLayoutHelper(graphSeries);
}
var nodeDataExtent = nodeData.getDataExtent('value');
// var edgeDataExtent = edgeData.getDataExtent('value');
var repulsion = forceModel.get('repulsion');
var edgeLength = forceModel.get('edgeLength');
var nodes = nodeData.mapArray('value', function (value, idx) {
var point = nodeData.getItemLayout(idx);
// var w = numberUtil.linearMap(value, nodeDataExtent, [0, 50]);
var rep = numberUtil.linearMap(value, nodeDataExtent, [0, repulsion]) || (repulsion / 2);
return {
w: rep,
rep: rep,
p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point
};
});
var edges = edgeData.mapArray('value', function (value, idx) {
var edge = graph.getEdgeByIndex(idx);
// var w = numberUtil.linearMap(value, edgeDataExtent, [0, 100]);
return {
n1: nodes[edge.node1.dataIndex],
n2: nodes[edge.node2.dataIndex],
d: edgeLength,
curveness: edge.getModel().get('lineStyle.normal.curveness') || 0
};
});
var coordSys = graphSeries.coordinateSystem;
var rect = coordSys.getBoundingRect();
var forceInstance = forceHelper(nodes, edges, {
rect: rect,
gravity: forceModel.get('gravity')
});
var oldStep = forceInstance.step;
forceInstance.step = function (cb) {
for (var i = 0, l = nodes.length; i < l; i++) {
if (nodes[i].fixed) {
// Write back to layout instance
vec2.copy(nodes[i].p, graph.getNodeByIndex(i).getLayout());
}
}
oldStep(function (nodes, edges, stopped) {
for (var i = 0, l = nodes.length; i < l; i++) {
if (!nodes[i].fixed) {
graph.getNodeByIndex(i).setLayout(nodes[i].p);
}
preservedPoints[nodeData.getId(i)] = nodes[i].p;
}
for (var i = 0, l = edges.length; i < l; i++) {
var e = edges[i];
var p1 = e.n1.p;
var p2 = e.n2.p;
var points = [p1, p2];
if (e.curveness > 0) {
points.push([
(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness,
(p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness
]);
}
graph.getEdgeByIndex(i).setLayout(points);
}
// Update layout
cb && cb(stopped);
});
};
graphSeries.forceLayout = forceInstance;
graphSeries.preservedPoints = preservedPoints;
// Step to get the layout
forceInstance.step();
}
else {
// Remove prev injected forceLayout instance
graphSeries.forceLayout = null;
}
});
};
});

View File

@@ -0,0 +1,35 @@
define(function (require) {
var echarts = require('../../echarts');
var roamHelper = require('../../action/roamHelper');
var actionInfo = {
type: 'graphRoam',
event: 'graphRoam',
update: 'none'
};
/**
* @payload
* @property {string} name Series name
* @property {number} [dx]
* @property {number} [dy]
* @property {number} [zoom]
* @property {number} [originX]
* @property {number} [originY]
*/
echarts.registerAction(actionInfo, function (payload, ecModel) {
ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
var res = roamHelper.updateCenterAndZoom(coordSys, payload);
seriesModel.setCenter
&& seriesModel.setCenter(res.center);
seriesModel.setZoom
&& seriesModel.setZoom(res.zoom);
});
});
});

View File

@@ -0,0 +1,28 @@
define(function (require) {
var simpleLayoutHelper = require('./simpleLayoutHelper');
var simpleLayoutEdge = require('./simpleLayoutEdge');
return function (ecModel, api) {
ecModel.eachSeriesByType('graph', function (seriesModel) {
var layout = seriesModel.get('layout');
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.type !== 'view') {
var data = seriesModel.getData();
data.each(coordSys.dimensions, function (x, y, idx) {
if (!isNaN(x) && !isNaN(y)) {
data.setItemLayout(idx, coordSys.dataToPoint([x, y]));
}
else {
// Also {Array.<number>}, not undefined to avoid if...else... statement
data.setItemLayout(idx, [NaN, NaN]);
}
});
simpleLayoutEdge(data.graph);
}
else if (!layout || layout === 'none') {
simpleLayoutHelper(seriesModel);
}
});
};
});

View File

@@ -0,0 +1,18 @@
define(function (require) {
var vec2 = require('zrender/core/vector');
return function (graph) {
graph.eachEdge(function (edge) {
var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0;
var p1 = vec2.clone(edge.node1.getLayout());
var p2 = vec2.clone(edge.node2.getLayout());
var points = [p1, p2];
if (curveness > 0) {
points.push([
(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness,
(p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness
]);
}
edge.setLayout(points);
});
};
});

View File

@@ -0,0 +1,19 @@
define(function (require) {
var simpleLayoutEdge = require('./simpleLayoutEdge');
return function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.type !== 'view') {
return;
}
var graph = seriesModel.getGraph();
graph.eachNode(function (node) {
var model = node.getModel();
node.setLayout([+model.get('x'), +model.get('y')]);
});
simpleLayoutEdge(graph);
};
});

5
vendors/echarts/src/chart/heatmap.js vendored Normal file
View File

@@ -0,0 +1,5 @@
define(function (require) {
require('./heatmap/HeatmapSeries');
require('./heatmap/HeatmapView');
});

View File

@@ -0,0 +1,148 @@
/**
* @file defines echarts Heatmap Chart
* @author Ovilia (me@zhangwenli.com)
* Inspired by https://github.com/mourner/simpleheat
*
* @module
*/
define(function (require) {
var GRADIENT_LEVELS = 256;
var zrUtil = require('zrender/core/util');
/**
* Heatmap Chart
*
* @class
*/
function Heatmap() {
var canvas = zrUtil.createCanvas();
this.canvas = canvas;
this.blurSize = 30;
this.pointSize = 20;
this.maxOpacity = 1;
this.minOpacity = 0;
this._gradientPixels = {};
}
Heatmap.prototype = {
/**
* Renders Heatmap and returns the rendered canvas
* @param {Array} data array of data, each has x, y, value
* @param {number} width canvas width
* @param {number} height canvas height
*/
update: function(data, width, height, normalize, colorFunc, isInRange) {
var brush = this._getBrush();
var gradientInRange = this._getGradient(data, colorFunc, 'inRange');
var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange');
var r = this.pointSize + this.blurSize;
var canvas = this.canvas;
var ctx = canvas.getContext('2d');
var len = data.length;
canvas.width = width;
canvas.height = height;
for (var i = 0; i < len; ++i) {
var p = data[i];
var x = p[0];
var y = p[1];
var value = p[2];
// calculate alpha using value
var alpha = normalize(value);
// draw with the circle brush with alpha
ctx.globalAlpha = alpha;
ctx.drawImage(brush, x - r, y - r);
}
// colorize the canvas using alpha value and set with gradient
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var pixels = imageData.data;
var offset = 0;
var pixelLen = pixels.length;
var minOpacity = this.minOpacity;
var maxOpacity = this.maxOpacity;
var diffOpacity = maxOpacity - minOpacity;
while(offset < pixelLen) {
var alpha = pixels[offset + 3] / 256;
var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4;
// Simple optimize to ignore the empty data
if (alpha > 0) {
var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange;
// Any alpha > 0 will be mapped to [minOpacity, maxOpacity]
alpha > 0 && (alpha = alpha * diffOpacity + minOpacity);
pixels[offset++] = gradient[gradientOffset];
pixels[offset++] = gradient[gradientOffset + 1];
pixels[offset++] = gradient[gradientOffset + 2];
pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256;
}
else {
offset += 4;
}
}
ctx.putImageData(imageData, 0, 0);
return canvas;
},
/**
* get canvas of a black circle brush used for canvas to draw later
* @private
* @returns {Object} circle brush canvas
*/
_getBrush: function() {
var brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas());
// set brush size
var r = this.pointSize + this.blurSize;
var d = r * 2;
brushCanvas.width = d;
brushCanvas.height = d;
var ctx = brushCanvas.getContext('2d');
ctx.clearRect(0, 0, d, d);
// in order to render shadow without the distinct circle,
// draw the distinct circle in an invisible place,
// and use shadowOffset to draw shadow in the center of the canvas
ctx.shadowOffsetX = d;
ctx.shadowBlur = this.blurSize;
// draw the shadow in black, and use alpha and shadow blur to generate
// color in color map
ctx.shadowColor = '#000';
// draw circle in the left to the canvas
ctx.beginPath();
ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
return brushCanvas;
},
/**
* get gradient color map
* @private
*/
_getGradient: function (data, colorFunc, state) {
var gradientPixels = this._gradientPixels;
var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));
var color = [];
var off = 0;
for (var i = 0; i < 256; i++) {
colorFunc[state](i / 255, true, color);
pixelsSingleState[off++] = color[0];
pixelsSingleState[off++] = color[1];
pixelsSingleState[off++] = color[2];
pixelsSingleState[off++] = color[3];
}
return pixelsSingleState;
}
};
return Heatmap;
});

View File

@@ -0,0 +1,38 @@
define(function (require) {
var SeriesModel = require('../../model/Series');
var createListFromArray = require('../helper/createListFromArray');
return SeriesModel.extend({
type: 'series.heatmap',
getInitialData: function (option, ecModel) {
return createListFromArray(option.data, this, ecModel);
},
defaultOption: {
// Cartesian2D or geo
coordinateSystem: 'cartesian2d',
zlevel: 0,
z: 2,
// Cartesian coordinate system
xAxisIndex: 0,
yAxisIndex: 0,
// Geo coordinate system
geoIndex: 0,
blurSize: 30,
pointSize: 20,
maxOpacity: 1,
minOpacity: 0
}
});
});

View File

@@ -0,0 +1,213 @@
define(function (require) {
var graphic = require('../../util/graphic');
var HeatmapLayer = require('./HeatmapLayer');
var zrUtil = require('zrender/core/util');
function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
var dataSpan = dataExtent[1] - dataExtent[0];
pieceList = zrUtil.map(pieceList, function (piece) {
return {
interval: [
(piece.interval[0] - dataExtent[0]) / dataSpan,
(piece.interval[1] - dataExtent[0]) / dataSpan
]
};
});
var len = pieceList.length;
var lastIndex = 0;
return function (val) {
// Try to find in the location of the last found
for (var i = lastIndex; i < len; i++) {
var interval = pieceList[i].interval;
if (interval[0] <= val && val <= interval[1]) {
lastIndex = i;
break;
}
}
if (i === len) { // Not found, back interation
for (var i = lastIndex - 1; i >= 0; i--) {
var interval = pieceList[i].interval;
if (interval[0] <= val && val <= interval[1]) {
lastIndex = i;
break;
}
}
}
return i >= 0 && i < len && selected[i];
};
}
function getIsInContinuousRange(dataExtent, range) {
var dataSpan = dataExtent[1] - dataExtent[0];
range = [
(range[0] - dataExtent[0]) / dataSpan,
(range[1] - dataExtent[0]) / dataSpan
];
return function (val) {
return val >= range[0] && val <= range[1];
};
}
function isGeoCoordSys(coordSys) {
var dimensions = coordSys.dimensions;
// Not use coorSys.type === 'geo' because coordSys maybe extended
return dimensions[0] === 'lng' && dimensions[1] === 'lat';
}
return require('../../echarts').extendChartView({
type: 'heatmap',
render: function (seriesModel, ecModel, api) {
var visualMapOfThisSeries;
ecModel.eachComponent('visualMap', function (visualMap) {
visualMap.eachTargetSeries(function (targetSeries) {
if (targetSeries === seriesModel) {
visualMapOfThisSeries = visualMap;
}
});
});
if (!visualMapOfThisSeries) {
throw new Error('Heatmap must use with visualMap');
}
this.group.removeAll();
var coordSys = seriesModel.coordinateSystem;
if (coordSys.type === 'cartesian2d') {
this._renderOnCartesian(coordSys, seriesModel, api);
}
else if (isGeoCoordSys(coordSys)) {
this._renderOnGeo(
coordSys, seriesModel, visualMapOfThisSeries, api
);
}
},
_renderOnCartesian: function (cartesian, seriesModel, api) {
var xAxis = cartesian.getAxis('x');
var yAxis = cartesian.getAxis('y');
var group = this.group;
if (!(xAxis.type === 'category' && yAxis.type === 'category')) {
throw new Error('Heatmap on cartesian must have two category axes');
}
if (!(xAxis.onBand && yAxis.onBand)) {
throw new Error('Heatmap on cartesian must have two axes with boundaryGap true');
}
var width = xAxis.getBandWidth();
var height = yAxis.getBandWidth();
var data = seriesModel.getData();
data.each(['x', 'y', 'z'], function (x, y, z, idx) {
var itemModel = data.getItemModel(idx);
var point = cartesian.dataToPoint([x, y]);
// Ignore empty data
if (isNaN(z)) {
return;
}
var rect = new graphic.Rect({
shape: {
x: point[0] - width / 2,
y: point[1] - height / 2,
width: width,
height: height
},
style: {
fill: data.getItemVisual(idx, 'color'),
opacity: data.getItemVisual(idx, 'opacity')
}
});
var style = itemModel.getModel('itemStyle.normal').getItemStyle(['color']);
var hoverStl = itemModel.getModel('itemStyle.emphasis').getItemStyle();
var labelModel = itemModel.getModel('label.normal');
var hoverLabelModel = itemModel.getModel('label.emphasis');
var rawValue = seriesModel.getRawValue(idx);
var defaultText = '-';
if (rawValue && rawValue[2] != null) {
defaultText = rawValue[2];
}
if (labelModel.get('show')) {
graphic.setText(style, labelModel);
style.text = seriesModel.getFormattedLabel(idx, 'normal') || defaultText;
}
if (hoverLabelModel.get('show')) {
graphic.setText(hoverStl, hoverLabelModel);
hoverStl.text = seriesModel.getFormattedLabel(idx, 'emphasis') || defaultText;
}
rect.setStyle(style);
graphic.setHoverStyle(rect, hoverStl);
group.add(rect);
data.setItemGraphicEl(idx, rect);
});
},
_renderOnGeo: function (geo, seriesModel, visualMapModel, api) {
var inRangeVisuals = visualMapModel.targetVisuals.inRange;
var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange;
// if (!visualMapping) {
// throw new Error('Data range must have color visuals');
// }
var data = seriesModel.getData();
var hmLayer = this._hmLayer || (this._hmLayer || new HeatmapLayer());
hmLayer.blurSize = seriesModel.get('blurSize');
hmLayer.pointSize = seriesModel.get('pointSize');
hmLayer.minOpacity = seriesModel.get('minOpacity');
hmLayer.maxOpacity = seriesModel.get('maxOpacity');
var rect = geo.getViewRect().clone();
var roamTransform = geo.getRoamTransform().transform;
rect.applyTransform(roamTransform);
// Clamp on viewport
var x = Math.max(rect.x, 0);
var y = Math.max(rect.y, 0);
var x2 = Math.min(rect.width + rect.x, api.getWidth());
var y2 = Math.min(rect.height + rect.y, api.getHeight());
var width = x2 - x;
var height = y2 - y;
var points = data.mapArray(['lng', 'lat', 'value'], function (lng, lat, value) {
var pt = geo.dataToPoint([lng, lat]);
pt[0] -= x;
pt[1] -= y;
pt.push(value);
return pt;
});
var dataExtent = visualMapModel.getExtent();
var isInRange = visualMapModel.type === 'visualMap.continuous'
? getIsInContinuousRange(dataExtent, visualMapModel.option.range)
: getIsInPiecewiseRange(
dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected
);
hmLayer.update(
points, width, height,
inRangeVisuals.color.getNormalizer(),
{
inRange: inRangeVisuals.color.getColorMapper(),
outOfRange: outOfRangeVisuals.color.getColorMapper()
},
isInRange
);
var img = new graphic.Image({
style: {
width: width,
height: height,
x: x,
y: y,
image: hmLayer.canvas
},
silent: true
});
this.group.add(img);
}
});
});

View File

@@ -0,0 +1,116 @@
/**
* @module echarts/chart/helper/EffectLine
*/
define(function (require) {
var graphic = require('../../util/graphic');
var Line = require('./Line');
var zrUtil = require('zrender/core/util');
var symbolUtil = require('../../util/symbol');
var curveUtil = require('zrender/core/curve');
/**
* @constructor
* @extends {module:zrender/graphic/Group}
* @alias {module:echarts/chart/helper/Line}
*/
function EffectLine(lineData, idx) {
graphic.Group.call(this);
var line = new Line(lineData, idx);
this.add(line);
this._updateEffectSymbol(lineData, idx);
}
var effectLineProto = EffectLine.prototype;
function setAnimationPoints(symbol, points) {
symbol.__p1 = points[0];
symbol.__p2 = points[1];
symbol.__cp1 = points[2] || [
(points[0][0] + points[1][0]) / 2,
(points[0][1] + points[1][1]) / 2
];
}
function updateSymbolPosition() {
var p1 = this.__p1;
var p2 = this.__p2;
var cp1 = this.__cp1;
var t = this.__t;
var pos = this.position;
var quadraticAt = curveUtil.quadraticAt;
var quadraticDerivativeAt = curveUtil.quadraticDerivativeAt;
pos[0] = quadraticAt(p1[0], cp1[0], p2[0], t);
pos[1] = quadraticAt(p1[1], cp1[1], p2[1], t);
// Tangent
var tx = quadraticDerivativeAt(p1[0], cp1[0], p2[0], t);
var ty = quadraticDerivativeAt(p1[1], cp1[1], p2[1], t);
this.rotation = -Math.atan2(ty, tx) - Math.PI / 2;
this.ignore = false;
}
effectLineProto._updateEffectSymbol = function (lineData, idx) {
var itemModel = lineData.getItemModel(idx);
var effectModel = itemModel.getModel('effect');
var size = effectModel.get('symbolSize');
var symbolType = effectModel.get('symbol');
if (!zrUtil.isArray(size)) {
size = [size, size];
}
var color = effectModel.get('color') || lineData.getItemVisual(idx, 'color');
var symbol = this.childAt(1);
var period = effectModel.get('period') * 1000;
if (this._symbolType !== symbolType || period !== this._period) {
symbol = symbolUtil.createSymbol(
symbolType, -0.5, -0.5, 1, 1, color
);
symbol.ignore = true;
symbol.z2 = 100;
this._symbolType = symbolType;
this._period = period;
this.add(symbol);
symbol.__t = 0;
symbol.animate('', true)
.when(period, {
__t: 1
})
.delay(idx / lineData.count() * period / 2)
.during(zrUtil.bind(updateSymbolPosition, symbol))
.start();
}
// Shadow color is same with color in default
symbol.setStyle('shadowColor', color);
symbol.setStyle(effectModel.getItemStyle(['color']));
symbol.attr('scale', size);
var points = lineData.getItemLayout(idx);
setAnimationPoints(symbol, points);
symbol.setColor(color);
symbol.attr('scale', size);
};
effectLineProto.updateData = function (lineData, idx) {
this.childAt(0).updateData(lineData, idx);
this._updateEffectSymbol(lineData, idx);
};
effectLineProto.updateLayout = function (lineData, idx) {
this.childAt(0).updateLayout(lineData, idx);
var symbol = this.childAt(1);
var points = lineData.getItemLayout(idx);
setAnimationPoints(symbol, points);
};
zrUtil.inherits(EffectLine, graphic.Group);
return EffectLine;
});

View File

@@ -0,0 +1,184 @@
/**
* Symbol with ripple effect
* @module echarts/chart/helper/EffectSymbol
*/
define(function (require) {
var zrUtil = require('zrender/core/util');
var symbolUtil = require('../../util/symbol');
var graphic = require('../../util/graphic');
var numberUtil = require('../../util/number');
var Symbol = require('./Symbol');
var Group = graphic.Group;
var EFFECT_RIPPLE_NUMBER = 3;
function normalizeSymbolSize(symbolSize) {
if (!zrUtil.isArray(symbolSize)) {
symbolSize = [+symbolSize, +symbolSize];
}
return symbolSize;
}
/**
* @constructor
* @param {module:echarts/data/List} data
* @param {number} idx
* @extends {module:zrender/graphic/Group}
*/
function EffectSymbol(data, idx) {
Group.call(this);
var symbol = new Symbol(data, idx);
var rippleGroup = new Group();
this.add(symbol);
this.add(rippleGroup);
rippleGroup.beforeUpdate = function () {
this.attr(symbol.getScale());
};
this.updateData(data, idx);
}
var effectSymbolProto = EffectSymbol.prototype;
effectSymbolProto.stopEffectAnimation = function () {
this.childAt(1).removeAll();
};
effectSymbolProto.startEffectAnimation = function (
period, brushType, rippleScale, effectOffset, z, zlevel
) {
var symbolType = this._symbolType;
var color = this._color;
var rippleGroup = this.childAt(1);
for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
var ripplePath = symbolUtil.createSymbol(
symbolType, -0.5, -0.5, 1, 1, color
);
ripplePath.attr({
style: {
stroke: brushType === 'stroke' ? color : null,
fill: brushType === 'fill' ? color : null,
strokeNoScale: true
},
z2: 99,
silent: true,
scale: [1, 1],
z: z,
zlevel: zlevel
});
var delay = -i / EFFECT_RIPPLE_NUMBER * period + effectOffset;
// TODO Configurable period
ripplePath.animate('', true)
.when(period, {
scale: [rippleScale, rippleScale]
})
.delay(delay)
.start();
ripplePath.animateStyle(true)
.when(period, {
opacity: 0
})
.delay(delay)
.start();
rippleGroup.add(ripplePath);
}
};
/**
* Highlight symbol
*/
effectSymbolProto.highlight = function () {
this.trigger('emphasis');
};
/**
* Downplay symbol
*/
effectSymbolProto.downplay = function () {
this.trigger('normal');
};
/**
* Update symbol properties
* @param {module:echarts/data/List} data
* @param {number} idx
*/
effectSymbolProto.updateData = function (data, idx) {
var seriesModel = data.hostModel;
this.childAt(0).updateData(data, idx);
var rippleGroup = this.childAt(1);
var itemModel = data.getItemModel(idx);
var symbolType = data.getItemVisual(idx, 'symbol');
var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
var color = data.getItemVisual(idx, 'color');
rippleGroup.attr('scale', symbolSize);
rippleGroup.traverse(function (ripplePath) {
ripplePath.attr({
fill: color
});
});
var symbolOffset = itemModel.getShallow('symbolOffset');
if (symbolOffset) {
var pos = rippleGroup.position;
pos[0] = numberUtil.parsePercent(symbolOffset[0], symbolSize[0]);
pos[1] = numberUtil.parsePercent(symbolOffset[1], symbolSize[1]);
}
rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
this._symbolType = symbolType;
this._color = color;
var showEffectOn = seriesModel.get('showEffectOn');
var rippleScale = itemModel.get('rippleEffect.scale');
var brushType = itemModel.get('rippleEffect.brushType');
var effectPeriod = itemModel.get('rippleEffect.period') * 1000;
var effectOffset = idx / data.count();
var z = itemModel.getShallow('z') || 0;
var zlevel = itemModel.getShallow('zlevel') || 0;
this.stopEffectAnimation();
if (showEffectOn === 'render') {
this.startEffectAnimation(
effectPeriod, brushType, rippleScale, effectOffset, z, zlevel
);
}
var symbol = this.childAt(0);
function onEmphasis() {
symbol.trigger('emphasis');
if (showEffectOn !== 'render') {
this.startEffectAnimation(
effectPeriod, brushType, rippleScale, effectOffset, z, zlevel
);
}
}
function onNormal() {
symbol.trigger('normal');
if (showEffectOn !== 'render') {
this.stopEffectAnimation();
}
}
this.on('mouseover', onEmphasis, this)
.on('mouseout', onNormal, this)
.on('emphasis', onEmphasis, this)
.on('normal', onNormal, this);
};
effectSymbolProto.fadeOut = function (cb) {
this.off('mouseover').off('mouseout').off('emphasis').off('normal');
cb && cb();
};
zrUtil.inherits(EffectSymbol, Group);
return EffectSymbol;
});

View File

@@ -0,0 +1,109 @@
define(function (require) {
var graphic = require('../../util/graphic');
var symbolUtil = require('../../util/symbol');
var zrUtil = require('zrender/core/util');
var LargeSymbolPath = graphic.extendShape({
shape: {
points: null,
sizes: null
},
symbolProxy: null,
buildPath: function (path, shape) {
var points = shape.points;
var sizes = shape.sizes;
var symbolProxy = this.symbolProxy;
var symbolProxyShape = symbolProxy.shape;
for (var i = 0; i < points.length; i++) {
var pt = points[i];
var size = sizes[i];
if (size[0] < 4) {
// Optimize for small symbol
path.rect(
pt[0] - size[0] / 2, pt[1] - size[1] / 2,
size[0], size[1]
);
}
else {
symbolProxyShape.x = pt[0] - size[0] / 2;
symbolProxyShape.y = pt[1] - size[1] / 2;
symbolProxyShape.width = size[0];
symbolProxyShape.height = size[1];
symbolProxy.buildPath(path, symbolProxyShape);
}
}
}
});
function LargeSymbolDraw() {
this.group = new graphic.Group();
this._symbolEl = new LargeSymbolPath({
silent: true
});
}
var largeSymbolProto = LargeSymbolDraw.prototype;
/**
* Update symbols draw by new data
* @param {module:echarts/data/List} data
*/
largeSymbolProto.updateData = function (data) {
this.group.removeAll();
var symbolEl = this._symbolEl;
var seriesModel = data.hostModel;
symbolEl.setShape({
points: data.mapArray(data.getItemLayout),
sizes: data.mapArray(
function (idx) {
var size = data.getItemVisual(idx, 'symbolSize');
if (!zrUtil.isArray(size)) {
size = [size, size];
}
return size;
}
)
});
// Create symbolProxy to build path for each data
symbolEl.symbolProxy = symbolUtil.createSymbol(
data.getVisual('symbol'), 0, 0, 0, 0
);
// Use symbolProxy setColor method
symbolEl.setColor = symbolEl.symbolProxy.setColor;
symbolEl.useStyle(
seriesModel.getModel('itemStyle.normal').getItemStyle(['color'])
);
var visualColor = data.getVisual('color');
if (visualColor) {
symbolEl.setColor(visualColor);
}
// Add back
this.group.add(this._symbolEl);
};
largeSymbolProto.updateLayout = function (seriesModel) {
var data = seriesModel.getData();
this._symbolEl.setShape({
points: data.mapArray(data.getItemLayout)
});
};
largeSymbolProto.remove = function () {
this.group.removeAll();
};
return LargeSymbolDraw;
});

313
vendors/echarts/src/chart/helper/Line.js vendored Normal file
View File

@@ -0,0 +1,313 @@
/**
* @module echarts/chart/helper/Line
*/
define(function (require) {
var symbolUtil = require('../../util/symbol');
var vector = require('zrender/core/vector');
// var matrix = require('zrender/core/matrix');
var LinePath = require('./LinePath');
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
var numberUtil = require('../../util/number');
var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];
function makeSymbolTypeKey(symbolCategory) {
return '_' + symbolCategory + 'Type';
}
/**
* @inner
*/
function createSymbol(name, lineData, idx) {
var color = lineData.getItemVisual(idx, 'color');
var symbolType = lineData.getItemVisual(idx, name);
var symbolSize = lineData.getItemVisual(idx, name + 'Size');
if (!symbolType || symbolType === 'none') {
return;
}
if (!zrUtil.isArray(symbolSize)) {
symbolSize = [symbolSize, symbolSize];
}
var symbolPath = symbolUtil.createSymbol(
symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2,
symbolSize[0], symbolSize[1], color
);
symbolPath.name = name;
return symbolPath;
}
function createLine(points) {
var line = new LinePath({
name: 'line'
});
setLinePoints(line.shape, points);
return line;
}
function setLinePoints(targetShape, points) {
var p1 = points[0];
var p2 = points[1];
var cp1 = points[2];
targetShape.x1 = p1[0];
targetShape.y1 = p1[1];
targetShape.x2 = p2[0];
targetShape.y2 = p2[1];
targetShape.percent = 1;
if (cp1) {
targetShape.cpx1 = cp1[0];
targetShape.cpy1 = cp1[1];
}
}
function updateSymbolAndLabelBeforeLineUpdate () {
var lineGroup = this;
var symbolFrom = lineGroup.childOfName('fromSymbol');
var symbolTo = lineGroup.childOfName('toSymbol');
var label = lineGroup.childOfName('label');
// Quick reject
if (!symbolFrom && !symbolTo && label.ignore) {
return;
}
var invScale = 1;
var parentNode = this.parent;
while (parentNode) {
if (parentNode.scale) {
invScale /= parentNode.scale[0];
}
parentNode = parentNode.parent;
}
var line = lineGroup.childOfName('line');
// If line not changed
// FIXME Parent scale changed
if (!this.__dirty && !line.__dirty) {
return;
}
var percent = line.shape.percent;
var fromPos = line.pointAt(0);
var toPos = line.pointAt(percent);
var d = vector.sub([], toPos, fromPos);
vector.normalize(d, d);
if (symbolFrom) {
symbolFrom.attr('position', fromPos);
var tangent = line.tangentAt(0);
symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
tangent[1], tangent[0]
));
symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
}
if (symbolTo) {
symbolTo.attr('position', toPos);
var tangent = line.tangentAt(1);
symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
tangent[1], tangent[0]
));
symbolTo.attr('scale', [invScale * percent, invScale * percent]);
}
if (!label.ignore) {
label.attr('position', toPos);
var textPosition;
var textAlign;
var textVerticalAlign;
var distance = 5 * invScale;
// End
if (label.__position === 'end') {
textPosition = [d[0] * distance + toPos[0], d[1] * distance + toPos[1]];
textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
}
// Middle
else if (label.__position === 'middle') {
var halfPercent = percent / 2;
var tangent = line.tangentAt(halfPercent);
var n = [tangent[1], -tangent[0]];
var cp = line.pointAt(halfPercent);
if (n[1] > 0) {
n[0] = -n[0];
n[1] = -n[1];
}
textPosition = [cp[0] + n[0] * distance, cp[1] + n[1] * distance];
textAlign = 'center';
textVerticalAlign = 'bottom';
var rotation = -Math.atan2(tangent[1], tangent[0]);
if (toPos[0] < fromPos[0]) {
rotation = Math.PI + rotation;
}
label.attr('rotation', rotation);
}
// Start
else {
textPosition = [-d[0] * distance + fromPos[0], -d[1] * distance + fromPos[1]];
textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
}
label.attr({
style: {
// Use the user specified text align and baseline first
textVerticalAlign: label.__verticalAlign || textVerticalAlign,
textAlign: label.__textAlign || textAlign
},
position: textPosition,
scale: [invScale, invScale]
});
}
}
/**
* @constructor
* @extends {module:zrender/graphic/Group}
* @alias {module:echarts/chart/helper/Line}
*/
function Line(lineData, idx) {
graphic.Group.call(this);
this._createLine(lineData, idx);
}
var lineProto = Line.prototype;
// Update symbol position and rotation
lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
lineProto._createLine = function (lineData, idx) {
var seriesModel = lineData.hostModel;
var linePoints = lineData.getItemLayout(idx);
var line = createLine(linePoints);
line.shape.percent = 0;
graphic.initProps(line, {
shape: {
percent: 1
}
}, seriesModel, idx);
this.add(line);
var label = new graphic.Text({
name: 'label'
});
this.add(label);
zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
var symbol = createSymbol(symbolCategory, lineData, idx);
// symbols must added after line to make sure
// it will be updated after line#update.
// Or symbol position and rotation update in line#beforeUpdate will be one frame slow
this.add(symbol);
this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
}, this);
this._updateCommonStl(lineData, idx);
};
lineProto.updateData = function (lineData, idx) {
var seriesModel = lineData.hostModel;
var line = this.childOfName('line');
var linePoints = lineData.getItemLayout(idx);
var target = {
shape: {}
};
setLinePoints(target.shape, linePoints);
graphic.updateProps(line, target, seriesModel, idx);
zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
var symbolType = lineData.getItemVisual(idx, symbolCategory);
var key = makeSymbolTypeKey(symbolCategory);
// Symbol changed
if (this[key] !== symbolType) {
var symbol = createSymbol(symbolCategory, lineData, idx);
this.remove(this.childOfName(symbolCategory));
this.add(symbol);
}
this[key] = symbolType;
}, this);
this._updateCommonStl(lineData, idx);
};
lineProto._updateCommonStl = function (lineData, idx) {
var seriesModel = lineData.hostModel;
var line = this.childOfName('line');
var itemModel = lineData.getItemModel(idx);
var labelModel = itemModel.getModel('label.normal');
var textStyleModel = labelModel.getModel('textStyle');
var labelHoverModel = itemModel.getModel('label.emphasis');
var textStyleHoverModel = labelHoverModel.getModel('textStyle');
var defaultText = numberUtil.round(seriesModel.getRawValue(idx));
if (isNaN(defaultText)) {
// Use name
defaultText = lineData.getName(idx);
}
line.useStyle(zrUtil.extend(
{
strokeNoScale: true,
fill: 'none',
stroke: lineData.getItemVisual(idx, 'color')
},
itemModel.getModel('lineStyle.normal').getLineStyle()
));
line.hoverStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle();
var defaultColor = lineData.getItemVisual(idx, 'color') || '#000';
var label = this.childOfName('label');
// label.afterUpdate = lineAfterUpdate;
label.setStyle({
text: labelModel.get('show')
? zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType),
defaultText
)
: '',
textFont: textStyleModel.getFont(),
fill: textStyleModel.getTextColor() || defaultColor
});
label.hoverStyle = {
text: labelHoverModel.get('show')
? zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
defaultText
)
: '',
textFont: textStyleHoverModel.getFont(),
fill: textStyleHoverModel.getTextColor() || defaultColor
};
label.__textAlign = textStyleModel.get('align');
label.__verticalAlign = textStyleModel.get('baseline');
label.__position = labelModel.get('position');
label.ignore = !label.style.text && !label.hoverStyle.text;
graphic.setHoverStyle(this);
};
lineProto.updateLayout = function (lineData, idx) {
var points = lineData.getItemLayout(idx);
var linePath = this.childOfName('line');
setLinePoints(linePath.shape, points);
linePath.dirty(true);
};
lineProto.setLinePoints = function (points) {
var linePath = this.childOfName('line');
setLinePoints(linePath.shape, points);
linePath.dirty();
};
zrUtil.inherits(Line, graphic.Group);
return Line;
});

View File

@@ -0,0 +1,85 @@
/**
* @module echarts/chart/helper/LineDraw
*/
define(function (require) {
var graphic = require('../../util/graphic');
var LineGroup = require('./Line');
function isPointNaN(pt) {
return isNaN(pt[0]) || isNaN(pt[1]);
}
function lineNeedsDraw(pts) {
return !isPointNaN(pts[0]) && !isPointNaN(pts[1]);
}
/**
* @alias module:echarts/component/marker/LineDraw
* @constructor
*/
function LineDraw(ctor) {
this._ctor = ctor || LineGroup;
this.group = new graphic.Group();
}
var lineDrawProto = LineDraw.prototype;
/**
* @param {module:echarts/data/List} lineData
*/
lineDrawProto.updateData = function (lineData) {
var oldLineData = this._lineData;
var group = this.group;
var LineCtor = this._ctor;
lineData.diff(oldLineData)
.add(function (idx) {
if (!lineNeedsDraw(lineData.getItemLayout(idx))) {
return;
}
var lineGroup = new LineCtor(lineData, idx);
lineData.setItemGraphicEl(idx, lineGroup);
group.add(lineGroup);
})
.update(function (newIdx, oldIdx) {
var lineGroup = oldLineData.getItemGraphicEl(oldIdx);
if (!lineNeedsDraw(lineData.getItemLayout(newIdx))) {
group.remove(lineGroup);
return;
}
if (!lineGroup) {
lineGroup = new LineCtor(lineData, newIdx);
}
else {
lineGroup.updateData(lineData, newIdx);
}
lineData.setItemGraphicEl(newIdx, lineGroup);
group.add(lineGroup);
})
.remove(function (idx) {
group.remove(oldLineData.getItemGraphicEl(idx));
})
.execute();
this._lineData = lineData;
};
lineDrawProto.updateLayout = function () {
var lineData = this._lineData;
lineData.eachItemGraphicEl(function (el, idx) {
el.updateLayout(lineData, idx);
}, this);
};
lineDrawProto.remove = function () {
this.group.removeAll();
};
return LineDraw;
});

View File

@@ -0,0 +1,52 @@
/**
* Line path for bezier and straight line draw
*/
define(function (require) {
var graphic = require('../../util/graphic');
var vec2 = require('zrender/core/vector');
var straightLineProto = graphic.Line.prototype;
var bezierCurveProto = graphic.BezierCurve.prototype;
function isLine(shape) {
return shape.cpx1 == null || shape.cpy1 == null;
}
return graphic.extendShape({
type: 'ec-line',
style: {
stroke: '#000',
fill: null
},
shape: {
x1: 0,
y1: 0,
x2: 0,
y2: 0,
percent: 1,
cpx1: null,
cpy1: null
},
buildPath: function (ctx, shape) {
(isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape);
},
pointAt: function (t) {
return isLine(this.shape)
? straightLineProto.pointAt.call(this, t)
: bezierCurveProto.pointAt.call(this, t);
},
tangentAt: function (t) {
var shape = this.shape;
var p = isLine(shape)
? [shape.x2 - shape.x1, shape.y2 - shape.y1]
: bezierCurveProto.tangentAt.call(this, t);
return vec2.normalize(p, p);
}
});
});

View File

@@ -0,0 +1,265 @@
/**
* @module echarts/chart/helper/Symbol
*/
define(function (require) {
var zrUtil = require('zrender/core/util');
var symbolUtil = require('../../util/symbol');
var graphic = require('../../util/graphic');
var numberUtil = require('../../util/number');
function normalizeSymbolSize(symbolSize) {
if (!zrUtil.isArray(symbolSize)) {
symbolSize = [+symbolSize, +symbolSize];
}
return symbolSize;
}
/**
* @constructor
* @alias {module:echarts/chart/helper/Symbol}
* @param {module:echarts/data/List} data
* @param {number} idx
* @extends {module:zrender/graphic/Group}
*/
function Symbol(data, idx) {
graphic.Group.call(this);
this.updateData(data, idx);
}
var symbolProto = Symbol.prototype;
function driftSymbol(dx, dy) {
this.parent.drift(dx, dy);
}
symbolProto._createSymbol = function (symbolType, data, idx) {
// Remove paths created before
this.removeAll();
var seriesModel = data.hostModel;
var color = data.getItemVisual(idx, 'color');
var symbolPath = symbolUtil.createSymbol(
symbolType, -0.5, -0.5, 1, 1, color
);
symbolPath.attr({
z2: 100,
culling: true,
scale: [0, 0]
});
// Rewrite drift method
symbolPath.drift = driftSymbol;
var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
graphic.initProps(symbolPath, {
scale: size
}, seriesModel, idx);
this._symbolType = symbolType;
this.add(symbolPath);
};
/**
* Stop animation
* @param {boolean} toLastFrame
*/
symbolProto.stopSymbolAnimation = function (toLastFrame) {
this.childAt(0).stopAnimation(toLastFrame);
};
/**
* Get scale(aka, current symbol size).
* Including the change caused by animation
*/
symbolProto.getScale = function () {
return this.childAt(0).scale;
};
/**
* Highlight symbol
*/
symbolProto.highlight = function () {
this.childAt(0).trigger('emphasis');
};
/**
* Downplay symbol
*/
symbolProto.downplay = function () {
this.childAt(0).trigger('normal');
};
/**
* @param {number} zlevel
* @param {number} z
*/
symbolProto.setZ = function (zlevel, z) {
var symbolPath = this.childAt(0);
symbolPath.zlevel = zlevel;
symbolPath.z = z;
};
symbolProto.setDraggable = function (draggable) {
var symbolPath = this.childAt(0);
symbolPath.draggable = draggable;
symbolPath.cursor = draggable ? 'move' : 'pointer';
};
/**
* Update symbol properties
* @param {module:echarts/data/List} data
* @param {number} idx
*/
symbolProto.updateData = function (data, idx) {
var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
var seriesModel = data.hostModel;
var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
if (symbolType !== this._symbolType) {
this._createSymbol(symbolType, data, idx);
}
else {
var symbolPath = this.childAt(0);
graphic.updateProps(symbolPath, {
scale: symbolSize
}, seriesModel, idx);
}
this._updateCommon(data, idx, symbolSize);
this._seriesModel = seriesModel;
};
// Update common properties
var normalStyleAccessPath = ['itemStyle', 'normal'];
var emphasisStyleAccessPath = ['itemStyle', 'emphasis'];
var normalLabelAccessPath = ['label', 'normal'];
var emphasisLabelAccessPath = ['label', 'emphasis'];
symbolProto._updateCommon = function (data, idx, symbolSize) {
var symbolPath = this.childAt(0);
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath);
var color = data.getItemVisual(idx, 'color');
// Reset style
if (symbolPath.type !== 'image') {
symbolPath.useStyle({
strokeNoScale: true
});
}
var elStyle = symbolPath.style;
var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
symbolPath.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
var symbolOffset = itemModel.getShallow('symbolOffset');
if (symbolOffset) {
var pos = symbolPath.position;
pos[0] = numberUtil.parsePercent(symbolOffset[0], symbolSize[0]);
pos[1] = numberUtil.parsePercent(symbolOffset[1], symbolSize[1]);
}
symbolPath.setColor(color);
zrUtil.extend(
elStyle,
// Color must be excluded.
// Because symbol provide setColor individually to set fill and stroke
normalItemStyleModel.getItemStyle(['color'])
);
var opacity = data.getItemVisual(idx, 'opacity');
if (opacity != null) {
elStyle.opacity = opacity;
}
var labelModel = itemModel.getModel(normalLabelAccessPath);
var hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
// Get last value dim
var dimensions = data.dimensions.slice();
var valueDim;
var dataType;
while (dimensions.length && (
valueDim = dimensions.pop(),
dataType = data.getDimensionInfo(valueDim).type,
dataType === 'ordinal' || dataType === 'time'
)) {} // jshint ignore:line
if (valueDim != null && labelModel.get('show')) {
graphic.setText(elStyle, labelModel, color);
elStyle.text = zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'normal'),
data.get(valueDim, idx)
);
}
else {
elStyle.text = '';
}
if (valueDim != null && hoverLabelModel.getShallow('show')) {
graphic.setText(hoverStyle, hoverLabelModel, color);
hoverStyle.text = zrUtil.retrieve(
seriesModel.getFormattedLabel(idx, 'emphasis'),
data.get(valueDim, idx)
);
}
else {
hoverStyle.text = '';
}
var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
symbolPath.off('mouseover')
.off('mouseout')
.off('emphasis')
.off('normal');
graphic.setHoverStyle(symbolPath, hoverStyle);
if (itemModel.getShallow('hoverAnimation')) {
var onEmphasis = function() {
var ratio = size[1] / size[0];
this.animateTo({
scale: [
Math.max(size[0] * 1.1, size[0] + 3),
Math.max(size[1] * 1.1, size[1] + 3 * ratio)
]
}, 400, 'elasticOut');
};
var onNormal = function() {
this.animateTo({
scale: size
}, 400, 'elasticOut');
};
symbolPath.on('mouseover', onEmphasis)
.on('mouseout', onNormal)
.on('emphasis', onEmphasis)
.on('normal', onNormal);
}
};
symbolProto.fadeOut = function (cb) {
var symbolPath = this.childAt(0);
// Avoid trigger hoverAnimation when fading
symbolPath.off('mouseover')
.off('mouseout')
.off('emphasis')
.off('normal');
// Not show text when animating
symbolPath.style.text = '';
graphic.updateProps(symbolPath, {
scale: [0, 0]
}, this._seriesModel, this.dataIndex, cb);
};
zrUtil.inherits(Symbol, graphic.Group);
return Symbol;
});

View File

@@ -0,0 +1,111 @@
/**
* @module echarts/chart/helper/SymbolDraw
*/
define(function (require) {
var graphic = require('../../util/graphic');
var Symbol = require('./Symbol');
/**
* @constructor
* @alias module:echarts/chart/helper/SymbolDraw
* @param {module:zrender/graphic/Group} [symbolCtor]
*/
function SymbolDraw(symbolCtor) {
this.group = new graphic.Group();
this._symbolCtor = symbolCtor || Symbol;
}
var symbolDrawProto = SymbolDraw.prototype;
function symbolNeedsDraw(data, idx, isIgnore) {
var point = data.getItemLayout(idx);
return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx))
&& data.getItemVisual(idx, 'symbol') !== 'none';
}
/**
* Update symbols draw by new data
* @param {module:echarts/data/List} data
* @param {Array.<boolean>} [isIgnore]
*/
symbolDrawProto.updateData = function (data, isIgnore) {
var group = this.group;
var seriesModel = data.hostModel;
var oldData = this._data;
var SymbolCtor = this._symbolCtor;
data.diff(oldData)
.add(function (newIdx) {
var point = data.getItemLayout(newIdx);
if (symbolNeedsDraw(data, newIdx, isIgnore)) {
var symbolEl = new SymbolCtor(data, newIdx);
symbolEl.attr('position', point);
data.setItemGraphicEl(newIdx, symbolEl);
group.add(symbolEl);
}
})
.update(function (newIdx, oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
var point = data.getItemLayout(newIdx);
if (!symbolNeedsDraw(data, newIdx, isIgnore)) {
group.remove(symbolEl);
return;
}
if (!symbolEl) {
symbolEl = new SymbolCtor(data, newIdx);
symbolEl.attr('position', point);
}
else {
symbolEl.updateData(data, newIdx);
graphic.updateProps(symbolEl, {
position: point
}, seriesModel);
}
// Add back
group.add(symbolEl);
data.setItemGraphicEl(newIdx, symbolEl);
})
.remove(function (oldIdx) {
var el = oldData.getItemGraphicEl(oldIdx);
el && el.fadeOut(function () {
group.remove(el);
});
})
.execute();
this._data = data;
};
symbolDrawProto.updateLayout = function () {
var data = this._data;
if (data) {
// Not use animation
data.eachItemGraphicEl(function (el, idx) {
el.attr('position', data.getItemLayout(idx));
});
}
};
symbolDrawProto.remove = function (enableAnimation) {
var group = this.group;
var data = this._data;
if (data) {
if (enableAnimation) {
data.eachItemGraphicEl(function (el) {
el.fadeOut(function () {
group.remove(el);
});
});
}
else {
group.removeAll();
}
}
};
return SymbolDraw;
});

View File

@@ -0,0 +1,215 @@
/**
* @module echarts/chart/helper/Symbol
*/
define(function (require) {
var zrUtil = require('zrender/core/util');
var graphic = require('../../util/graphic');
var Path = require('zrender/graphic/Path');
var WhiskerPath = Path.extend({
type: 'whiskerInBox',
shape: {},
buildPath: function (ctx, shape) {
for (var i in shape) {
if (i.indexOf('ends') === 0) {
var pts = shape[i];
ctx.moveTo(pts[0][0], pts[0][1]);
ctx.lineTo(pts[1][0], pts[1][1]);
}
}
}
});
/**
* @constructor
* @alias {module:echarts/chart/helper/WhiskerBox}
* @param {module:echarts/data/List} data
* @param {number} idx
* @param {Function} styleUpdater
* @param {boolean} isInit
* @extends {module:zrender/graphic/Group}
*/
function WhiskerBox(data, idx, styleUpdater, isInit) {
graphic.Group.call(this);
/**
* @type {number}
* @readOnly
*/
this.bodyIndex;
/**
* @type {number}
* @readOnly
*/
this.whiskerIndex;
/**
* @type {Function}
*/
this.styleUpdater = styleUpdater;
this._createContent(data, idx, isInit);
this.updateData(data, idx, isInit);
/**
* Last series model.
* @type {module:echarts/model/Series}
*/
this._seriesModel;
}
var whiskerBoxProto = WhiskerBox.prototype;
whiskerBoxProto._createContent = function (data, idx, isInit) {
var itemLayout = data.getItemLayout(idx);
var constDim = itemLayout.chartLayout === 'horizontal' ? 1 : 0;
var count = 0;
// Whisker element.
this.add(new graphic.Polygon({
shape: {
points: isInit
? transInit(itemLayout.bodyEnds, constDim, itemLayout)
: itemLayout.bodyEnds
},
style: {strokeNoScale: true},
z2: 100
}));
this.bodyIndex = count++;
// Box element.
var whiskerEnds = zrUtil.map(itemLayout.whiskerEnds, function (ends) {
return isInit ? transInit(ends, constDim, itemLayout) : ends;
});
this.add(new WhiskerPath({
shape: makeWhiskerEndsShape(whiskerEnds),
style: {strokeNoScale: true},
z2: 100
}));
this.whiskerIndex = count++;
};
function transInit(points, dim, itemLayout) {
return zrUtil.map(points, function (point) {
point = point.slice();
point[dim] = itemLayout.initBaseline;
return point;
});
}
function makeWhiskerEndsShape(whiskerEnds) {
// zr animation only support 2-dim array.
var shape = {};
zrUtil.each(whiskerEnds, function (ends, i) {
shape['ends' + i] = ends;
});
return shape;
}
/**
* Update symbol properties
* @param {module:echarts/data/List} data
* @param {number} idx
*/
whiskerBoxProto.updateData = function (data, idx, isInit) {
var seriesModel = this._seriesModel = data.hostModel;
var itemLayout = data.getItemLayout(idx);
var updateMethod = graphic[isInit ? 'initProps' : 'updateProps'];
// this.childAt(this.bodyIndex).stopAnimation(true);
// this.childAt(this.whiskerIndex).stopAnimation(true);
updateMethod(
this.childAt(this.bodyIndex),
{shape: {points: itemLayout.bodyEnds}},
seriesModel, idx
);
updateMethod(
this.childAt(this.whiskerIndex),
{shape: makeWhiskerEndsShape(itemLayout.whiskerEnds)},
seriesModel, idx
);
this.styleUpdater.call(null, this, data, idx);
};
zrUtil.inherits(WhiskerBox, graphic.Group);
/**
* @constructor
* @alias module:echarts/chart/helper/WhiskerBoxDraw
*/
function WhiskerBoxDraw(styleUpdater) {
this.group = new graphic.Group();
this.styleUpdater = styleUpdater;
}
var whiskerBoxDrawProto = WhiskerBoxDraw.prototype;
/**
* Update symbols draw by new data
* @param {module:echarts/data/List} data
*/
whiskerBoxDrawProto.updateData = function (data) {
var group = this.group;
var oldData = this._data;
var styleUpdater = this.styleUpdater;
data.diff(oldData)
.add(function (newIdx) {
if (data.hasValue(newIdx)) {
var symbolEl = new WhiskerBox(data, newIdx, styleUpdater, true);
data.setItemGraphicEl(newIdx, symbolEl);
group.add(symbolEl);
}
})
.update(function (newIdx, oldIdx) {
var symbolEl = oldData.getItemGraphicEl(oldIdx);
// Empty data
if (!data.hasValue(newIdx)) {
group.remove(symbolEl);
return;
}
if (!symbolEl) {
symbolEl = new WhiskerBox(data, newIdx, styleUpdater);
}
else {
symbolEl.updateData(data, newIdx);
}
// Add back
group.add(symbolEl);
data.setItemGraphicEl(newIdx, symbolEl);
})
.remove(function (oldIdx) {
var el = oldData.getItemGraphicEl(oldIdx);
el && group.remove(el);
})
.execute();
this._data = data;
};
/**
* Remove symbols.
* @param {module:echarts/data/List} data
*/
whiskerBoxDrawProto.remove = function () {
var group = this.group;
var data = this._data;
this._data = null;
data && data.eachItemGraphicEl(function (el) {
el && group.remove(el);
});
};
return WhiskerBoxDraw;
});

View File

@@ -0,0 +1,68 @@
define(function (require) {
var List = require('../../data/List');
var Graph = require('../../data/Graph');
var linkList = require('../../data/helper/linkList');
var completeDimensions = require('../../data/helper/completeDimensions');
var CoordinateSystem = require('../../CoordinateSystem');
var zrUtil = require('zrender/core/util');
var createListFromArray = require('./createListFromArray');
return function (nodes, edges, hostModel, directed, beforeLink) {
var graph = new Graph(directed);
for (var i = 0; i < nodes.length; i++) {
graph.addNode(zrUtil.retrieve(
// Id, name, dataIndex
nodes[i].id, nodes[i].name, i
), i);
}
var linkNameList = [];
var validEdges = [];
for (var i = 0; i < edges.length; i++) {
var link = edges[i];
var source = link.source;
var target = link.target;
// addEdge may fail when source or target not exists
if (graph.addEdge(source, target, i)) {
validEdges.push(link);
linkNameList.push(zrUtil.retrieve(link.id, source + ' > ' + target));
}
}
var coordSys = hostModel.get('coordinateSystem');
var nodeData;
if (coordSys === 'cartesian2d' || coordSys === 'polar') {
nodeData = createListFromArray(nodes, hostModel, hostModel.ecModel);
}
else {
// FIXME
var coordSysCtor = CoordinateSystem.get(coordSys);
// FIXME
var dimensionNames = completeDimensions(
((coordSysCtor && coordSysCtor.type !== 'view') ? (coordSysCtor.dimensions || []) : []).concat(['value']),
nodes
);
nodeData = new List(dimensionNames, hostModel);
nodeData.initData(nodes);
}
var edgeData = new List(['value'], hostModel);
edgeData.initData(validEdges, linkNameList);
beforeLink && beforeLink(nodeData, edgeData);
linkList({
mainData: nodeData,
struct: graph,
structAttr: 'graph',
datas: {node: nodeData, edge: edgeData},
datasAttr: {node: 'data', edge: 'edgeData'}
});
// Update dataIndex of nodes and edges because invalid edge may be removed
graph.update();
return graph;
};
});

View File

@@ -0,0 +1,92 @@
define(function (require) {
var List = require('../../data/List');
var Graph = require('../../data/Graph');
var linkList = require('../../data/helper/linkList');
var completeDimensions = require('../../data/helper/completeDimensions');
var CoordinateSystem = require('../../CoordinateSystem');
var zrUtil = require('zrender/core/util');
var createListFromArray = require('./createListFromArray');
/**
* 从邻接矩阵生成
* ```
* TARGET
* -1--2--3--4--5-
* 1| x x x x x
* 2| x x x x x
* 3| x x x x x SOURCE
* 4| x x x x x
* 5| x x x x x
* ```
*
* @param {Array.<Object>} nodes 节点信息
* @param {Array} matrix 邻接矩阵
* @param {module:echarts/model/Series}
* @param {boolean} directed 是否是有向图
* @return {module:echarts/data/Graph}
*/
return function (nodes, matrix, hostModel, directed) {
var graph = new Graph(directed);
for (var i = 0; i < nodes.length; i++) {
graph.addNode(zrUtil.retrieve(
// Id, name, dataIndex
nodes[i].id, nodes[i].name, i
), i);
}
var size = matrix.length;
var links = [];
var linkCount = 0;
for (var i = 0; i < size; i++) {
for (var j = 0; j < size; j++) {
var val = matrix[i][j];
if (val === 0) {
continue;
}
var n1 = graph.nodes[i];
var n2 = graph.nodes[j];
var edge = graph.addEdge(n1, n2, linkCount);
if (edge) {
linkCount++;
links.push({
value: val
});
}
}
}
var coordSys = hostModel.get('coordinateSystem');
var nodeData;
if (coordSys === 'cartesian2d' || coordSys === 'polar') {
nodeData = createListFromArray(nodes, hostModel, hostModel.ecModel);
}
else {
// FIXME
var coordSysCtor = CoordinateSystem.get(coordSys);
// FIXME
var dimensionNames = completeDimensions(
((coordSysCtor && coordSysCtor.type !== 'view') ? (coordSysCtor.dimensions || []) : []).concat(['value']),
nodes
);
nodeData = new List(dimensionNames, hostModel);
nodeData.initData(nodes);
}
var edgeData = new List(['value'], hostModel);
edgeData.initData(links);
linkList({
mainData: nodeData,
struct: graph,
structAttr: 'graph',
datas: {node: nodeData, edge: edgeData},
datasAttr: {node: 'data', edge: 'edgeData'}
});
// Update dataIndex of nodes and edges because invalid edge may be removed
graph.update();
return graph;
};
});

View File

@@ -0,0 +1,229 @@
define(function(require) {
'use strict';
var List = require('../../data/List');
var completeDimensions = require('../../data/helper/completeDimensions');
var zrUtil = require('zrender/core/util');
var modelUtil = require('../../util/model');
var CoordinateSystem = require('../../CoordinateSystem');
var getDataItemValue = modelUtil.getDataItemValue;
var converDataValue = modelUtil.converDataValue;
function firstDataNotNull(data) {
var i = 0;
while (i < data.length && data[i] == null) {
i++;
}
return data[i];
}
function ifNeedCompleteOrdinalData(data) {
var sampleItem = firstDataNotNull(data);
return sampleItem != null
&& !zrUtil.isArray(getDataItemValue(sampleItem));
}
/**
* Helper function to create a list from option data
*/
function createListFromArray(data, seriesModel, ecModel) {
// If data is undefined
data = data || [];
if (!zrUtil.isArray(data)) {
throw new Error('Invalid data.');
}
var coordSysName = seriesModel.get('coordinateSystem');
var creator = creators[coordSysName];
var registeredCoordSys = CoordinateSystem.get(coordSysName);
// FIXME
var result = creator && creator(data, seriesModel, ecModel);
var dimensions = result && result.dimensions;
if (!dimensions) {
// Get dimensions from registered coordinate system
dimensions = (registeredCoordSys && registeredCoordSys.dimensions) || ['x', 'y'];
dimensions = completeDimensions(dimensions, data, dimensions.concat(['value']));
}
var categoryAxisModel = result && result.categoryAxisModel;
var categories;
var categoryDimIndex = dimensions[0].type === 'ordinal'
? 0 : (dimensions[1].type === 'ordinal' ? 1 : -1);
var list = new List(dimensions, seriesModel);
var nameList = createNameList(result, data);
var dimValueGetter = (categoryAxisModel && ifNeedCompleteOrdinalData(data))
? function (itemOpt, dimName, dataIndex, dimIndex) {
// Use dataIndex as ordinal value in categoryAxis
return dimIndex === categoryDimIndex
? dataIndex
: converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);
}
: function (itemOpt, dimName, dataIndex, dimIndex) {
var value = getDataItemValue(itemOpt);
var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]);
if (categoryDimIndex === dimIndex) {
// If given value is a category string
if (typeof val === 'string') {
// Lazy get categories
categories = categories || categoryAxisModel.getCategories();
val = zrUtil.indexOf(categories, val);
if (val < 0 && !isNaN(val)) {
// In case some one write '1', '2' istead of 1, 2
val = +val;
}
}
}
return val;
};
list.initData(data, nameList, dimValueGetter);
return list;
}
function isStackable(axisType) {
return axisType !== 'category' && axisType !== 'time';
}
function getDimTypeByAxis(axisType) {
return axisType === 'category'
? 'ordinal'
: axisType === 'time'
? 'time'
: 'float';
}
/**
* Creaters for each coord system.
* @return {Object} {dimensions, categoryAxisModel};
*/
var creators = {
cartesian2d: function (data, seriesModel, ecModel) {
var xAxisModel = ecModel.getComponent('xAxis', seriesModel.get('xAxisIndex'));
var yAxisModel = ecModel.getComponent('yAxis', seriesModel.get('yAxisIndex'));
if (!xAxisModel || !yAxisModel) {
throw new Error('Axis option not found');
}
var xAxisType = xAxisModel.get('type');
var yAxisType = yAxisModel.get('type');
var dimensions = [
{
name: 'x',
type: getDimTypeByAxis(xAxisType),
stackable: isStackable(xAxisType)
},
{
name: 'y',
// If two category axes
type: getDimTypeByAxis(yAxisType),
stackable: isStackable(yAxisType)
}
];
var isXAxisCateogry = xAxisType === 'category';
completeDimensions(dimensions, data, ['x', 'y', 'z']);
return {
dimensions: dimensions,
categoryIndex: isXAxisCateogry ? 0 : 1,
categoryAxisModel: isXAxisCateogry
? xAxisModel
: (yAxisType === 'category' ? yAxisModel : null)
};
},
polar: function (data, seriesModel, ecModel) {
var polarIndex = seriesModel.get('polarIndex') || 0;
var axisFinder = function (axisModel) {
return axisModel.get('polarIndex') === polarIndex;
};
var angleAxisModel = ecModel.findComponents({
mainType: 'angleAxis', filter: axisFinder
})[0];
var radiusAxisModel = ecModel.findComponents({
mainType: 'radiusAxis', filter: axisFinder
})[0];
if (!angleAxisModel || !radiusAxisModel) {
throw new Error('Axis option not found');
}
var radiusAxisType = radiusAxisModel.get('type');
var angleAxisType = angleAxisModel.get('type');
var dimensions = [
{
name: 'radius',
type: getDimTypeByAxis(radiusAxisType),
stackable: isStackable(radiusAxisType)
},
{
name: 'angle',
type: getDimTypeByAxis(angleAxisType),
stackable: isStackable(angleAxisType)
}
];
var isAngleAxisCateogry = angleAxisType === 'category';
completeDimensions(dimensions, data, ['radius', 'angle', 'value']);
return {
dimensions: dimensions,
categoryIndex: isAngleAxisCateogry ? 1 : 0,
categoryAxisModel: isAngleAxisCateogry
? angleAxisModel
: (radiusAxisType === 'category' ? radiusAxisModel : null)
};
},
geo: function (data, seriesModel, ecModel) {
// TODO Region
// 多个散点图系列在同一个地区的时候
return {
dimensions: completeDimensions([
{name: 'lng'},
{name: 'lat'}
], data, ['lng', 'lat', 'value'])
};
}
};
function createNameList(result, data) {
var nameList = [];
if (result && result.categoryAxisModel) {
// FIXME Two category axis
var categories = result.categoryAxisModel.getCategories();
if (categories) {
var dataLen = data.length;
// Ordered data is given explicitly like
// [[3, 0.2], [1, 0.3], [2, 0.15]]
// or given scatter data,
// pick the category
if (zrUtil.isArray(data[0]) && data[0].length > 1) {
nameList = [];
for (var i = 0; i < dataLen; i++) {
nameList[i] = categories[data[i][result.categoryIndex || 0]];
}
}
else {
nameList = categories.slice(0);
}
}
}
return nameList;
}
return createListFromArray;
});

View File

@@ -0,0 +1,140 @@
define(function(require) {
'use strict';
var List = require('../../data/List');
var completeDimensions = require('../../data/helper/completeDimensions');
var WhiskerBoxDraw = require('../helper/WhiskerBoxDraw');
var zrUtil = require('zrender/core/util');
function getItemValue(item) {
return item.value == null ? item : item.value;
}
var seriesModelMixin = {
/**
* @private
* @type {string}
*/
_baseAxisDim: null,
/**
* @override
*/
getInitialData: function (option, ecModel) {
// When both types of xAxis and yAxis are 'value', layout is
// needed to be specified by user. Otherwise, layout can be
// judged by which axis is category.
var categories;
var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex'));
var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex'));
var xAxisType = xAxisModel.get('type');
var yAxisType = yAxisModel.get('type');
var addOrdinal;
// FIXME
// 考虑时间轴
if (xAxisType === 'category') {
option.layout = 'horizontal';
categories = xAxisModel.getCategories();
addOrdinal = true;
}
else if (yAxisType === 'category') {
option.layout = 'vertical';
categories = yAxisModel.getCategories();
addOrdinal = true;
}
else {
option.layout = option.layout || 'horizontal';
}
this._baseAxisDim = option.layout === 'horizontal' ? 'x' : 'y';
var data = option.data;
var dimensions = this.dimensions = ['base'].concat(this.valueDimensions);
completeDimensions(dimensions, data);
var list = new List(dimensions, this);
list.initData(data, categories ? categories.slice() : null, function (dataItem, dimName, idx, dimIdx) {
var value = getItemValue(dataItem);
return addOrdinal ? (dimName === 'base' ? idx : value[dimIdx - 1]) : value[dimIdx];
});
return list;
},
/**
* Used by Gird.
* @param {string} axisDim 'x' or 'y'
* @return {Array.<string>} dimensions on the axis.
*/
coordDimToDataDim: function (axisDim) {
var dims = this.valueDimensions.slice();
var baseDim = ['base'];
var map = {
horizontal: {x: baseDim, y: dims},
vertical: {x: dims, y: baseDim}
};
return map[this.get('layout')][axisDim];
},
/**
* @override
* @param {string|number} dataDim
* @return {string} coord dimension
*/
dataDimToCoordDim: function (dataDim) {
var dim;
zrUtil.each(['x', 'y'], function (coordDim, index) {
var dataDims = this.coordDimToDataDim(coordDim);
if (zrUtil.indexOf(dataDims, dataDim) >= 0) {
dim = coordDim;
}
}, this);
return dim;
},
/**
* If horizontal, base axis is x, otherwise y.
* @override
*/
getBaseAxis: function () {
var dim = this._baseAxisDim;
return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis;
}
};
var viewMixin = {
init: function () {
/**
* Old data.
* @private
* @type {module:echarts/chart/helper/WhiskerBoxDraw}
*/
var whiskerBoxDraw = this._whiskerBoxDraw = new WhiskerBoxDraw(
this.getStyleUpdater()
);
this.group.add(whiskerBoxDraw.group);
},
render: function (seriesModel, ecModel, api) {
this._whiskerBoxDraw.updateData(seriesModel.getData());
},
remove: function (ecModel) {
this._whiskerBoxDraw.remove();
}
};
return {
seriesModelMixin: seriesModelMixin,
viewMixin: viewMixin
};
});

23
vendors/echarts/src/chart/line.js vendored Normal file
View File

@@ -0,0 +1,23 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
require('./line/LineSeries');
require('./line/LineView');
echarts.registerVisualCoding('chart', zrUtil.curry(
require('../visual/symbol'), 'line', 'circle', 'line'
));
echarts.registerLayout(zrUtil.curry(
require('../layout/points'), 'line'
));
// Down sample after filter
echarts.registerProcessor('statistic', zrUtil.curry(
require('../processor/dataSample'), 'line'
));
// In case developer forget to include grid component
require('../component/grid');
});

View File

@@ -0,0 +1,74 @@
define(function(require) {
'use strict';
var createListFromArray = require('../helper/createListFromArray');
var SeriesModel = require('../../model/Series');
return SeriesModel.extend({
type: 'series.line',
dependencies: ['grid', 'polar'],
getInitialData: function (option, ecModel) {
return createListFromArray(option.data, this, ecModel);
},
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
coordinateSystem: 'cartesian2d',
legendHoverLink: true,
hoverAnimation: true,
// stack: null
xAxisIndex: 0,
yAxisIndex: 0,
polarIndex: 0,
// If clip the overflow value
clipOverflow: true,
label: {
normal: {
position: 'top'
}
},
// itemStyle: {
// normal: {},
// emphasis: {}
// },
lineStyle: {
normal: {
width: 2,
type: 'solid'
}
},
// areaStyle: {},
smooth: false,
smoothMonotone: null,
// 拐点图形类型
symbol: 'emptyCircle',
// 拐点图形大小
symbolSize: 4,
// 拐点图形旋转控制
symbolRotate: null,
// 是否显示 symbol, 只有在 tooltip hover 的时候显示
showSymbol: true,
// 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略)
showAllSymbol: false,
// 是否连接断点
connectNulls: false,
// 数据过滤,'average', 'max', 'min', 'sum'
sampling: 'none',
animationEasing: 'linear'
}
});
});

View File

@@ -0,0 +1,550 @@
define(function(require) {
'use strict';
var zrUtil = require('zrender/core/util');
var SymbolDraw = require('../helper/SymbolDraw');
var Symbol = require('../helper/Symbol');
var lineAnimationDiff = require('./lineAnimationDiff');
var graphic = require('../../util/graphic');
var polyHelper = require('./poly');
var ChartView = require('../../view/Chart');
function isPointsSame(points1, points2) {
if (points1.length !== points2.length) {
return;
}
for (var i = 0; i < points1.length; i++) {
var p1 = points1[i];
var p2 = points2[i];
if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
return;
}
}
return true;
}
function getSmooth(smooth) {
return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0);
}
function getAxisExtentWithGap(axis) {
var extent = axis.getGlobalExtent();
if (axis.onBand) {
// Remove extra 1px to avoid line miter in clipped edge
var halfBandWidth = axis.getBandWidth() / 2 - 1;
var dir = extent[1] > extent[0] ? 1 : -1;
extent[0] += dir * halfBandWidth;
extent[1] -= dir * halfBandWidth;
}
return extent;
}
function sign(val) {
return val >= 0 ? 1 : -1;
}
/**
* @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
* @param {module:echarts/data/List} data
* @param {Array.<Array.<number>>} points
* @private
*/
function getStackedOnPoints(coordSys, data) {
var baseAxis = coordSys.getBaseAxis();
var valueAxis = coordSys.getOtherAxis(baseAxis);
var valueStart = baseAxis.onZero
? 0 : valueAxis.scale.getExtent()[0];
var valueDim = valueAxis.dim;
var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
return data.mapArray([valueDim], function (val, idx) {
var stackedOnSameSign;
var stackedOn = data.stackedOn;
// Find first stacked value with same sign
while (stackedOn &&
sign(stackedOn.get(valueDim, idx)) === sign(val)
) {
stackedOnSameSign = stackedOn;
break;
}
var stackedData = [];
stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
stackedData[1 - baseDataOffset] = stackedOnSameSign
? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
return coordSys.dataToPoint(stackedData);
}, true);
}
function queryDataIndex(data, payload) {
if (payload.dataIndex != null) {
return payload.dataIndex;
}
else if (payload.name != null) {
return data.indexOfName(payload.name);
}
}
function createGridClipShape(cartesian, hasAnimation, seriesModel) {
var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
var isHorizontal = cartesian.getBaseAxis().isHorizontal();
var x = Math.min(xExtent[0], xExtent[1]);
var y = Math.min(yExtent[0], yExtent[1]);
var width = Math.max(xExtent[0], xExtent[1]) - x;
var height = Math.max(yExtent[0], yExtent[1]) - y;
var lineWidth = seriesModel.get('lineStyle.normal.width') || 2;
// Expand clip shape to avoid clipping when line value exceeds axis
var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height);
if (isHorizontal) {
y -= expandSize;
height += expandSize * 2;
}
else {
x -= expandSize;
width += expandSize * 2;
}
var clipPath = new graphic.Rect({
shape: {
x: x,
y: y,
width: width,
height: height
}
});
if (hasAnimation) {
clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
graphic.initProps(clipPath, {
shape: {
width: width,
height: height
}
}, seriesModel);
}
return clipPath;
}
function createPolarClipShape(polar, hasAnimation, seriesModel) {
var angleAxis = polar.getAngleAxis();
var radiusAxis = polar.getRadiusAxis();
var radiusExtent = radiusAxis.getExtent();
var angleExtent = angleAxis.getExtent();
var RADIAN = Math.PI / 180;
var clipPath = new graphic.Sector({
shape: {
cx: polar.cx,
cy: polar.cy,
r0: radiusExtent[0],
r: radiusExtent[1],
startAngle: -angleExtent[0] * RADIAN,
endAngle: -angleExtent[1] * RADIAN,
clockwise: angleAxis.inverse
}
});
if (hasAnimation) {
clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
graphic.initProps(clipPath, {
shape: {
endAngle: -angleExtent[1] * RADIAN
}
}, seriesModel);
}
return clipPath;
}
function createClipShape(coordSys, hasAnimation, seriesModel) {
return coordSys.type === 'polar'
? createPolarClipShape(coordSys, hasAnimation, seriesModel)
: createGridClipShape(coordSys, hasAnimation, seriesModel);
}
return ChartView.extend({
type: 'line',
init: function () {
var lineGroup = new graphic.Group();
var symbolDraw = new SymbolDraw();
this.group.add(symbolDraw.group);
this._symbolDraw = symbolDraw;
this._lineGroup = lineGroup;
},
render: function (seriesModel, ecModel, api) {
var coordSys = seriesModel.coordinateSystem;
var group = this.group;
var data = seriesModel.getData();
var lineStyleModel = seriesModel.getModel('lineStyle.normal');
var areaStyleModel = seriesModel.getModel('areaStyle.normal');
var points = data.mapArray(data.getItemLayout, true);
var isCoordSysPolar = coordSys.type === 'polar';
var prevCoordSys = this._coordSys;
var symbolDraw = this._symbolDraw;
var polyline = this._polyline;
var polygon = this._polygon;
var lineGroup = this._lineGroup;
var hasAnimation = seriesModel.get('animation');
var isAreaChart = !areaStyleModel.isEmpty();
var stackedOnPoints = getStackedOnPoints(coordSys, data);
var showSymbol = seriesModel.get('showSymbol');
var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol')
&& this._getSymbolIgnoreFunc(data, coordSys);
// Remove temporary symbols
var oldData = this._data;
oldData && oldData.eachItemGraphicEl(function (el, idx) {
if (el.__temp) {
group.remove(el);
oldData.setItemGraphicEl(idx, null);
}
});
// Remove previous created symbols if showSymbol changed to false
if (!showSymbol) {
symbolDraw.remove();
}
group.add(lineGroup);
// Initialization animation or coordinate system changed
if (
!(polyline && prevCoordSys.type === coordSys.type)
) {
showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
polyline = this._newPolyline(points, coordSys, hasAnimation);
if (isAreaChart) {
polygon = this._newPolygon(
points, stackedOnPoints,
coordSys, hasAnimation
);
}
lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
}
else {
if (isAreaChart && !polygon) {
// If areaStyle is added
polygon = this._newPolygon(
points, stackedOnPoints,
coordSys, hasAnimation
);
}
else if (polygon && !isAreaChart) {
// If areaStyle is removed
lineGroup.remove(polygon);
polygon = this._polygon = null;
}
// Update clipPath
lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));
// Always update, or it is wrong in the case turning on legend
// because points are not changed
showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
// Stop symbol animation and sync with line points
// FIXME performance?
data.eachItemGraphicEl(function (el) {
el.stopAnimation(true);
});
// In the case data zoom triggerred refreshing frequently
// Data may not change if line has a category axis. So it should animate nothing
if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
|| !isPointsSame(this._points, points)
) {
if (hasAnimation) {
this._updateAnimation(
data, stackedOnPoints, coordSys, api
);
}
else {
polyline.setShape({
points: points
});
polygon && polygon.setShape({
points: points,
stackedOnPoints: stackedOnPoints
});
}
}
}
polyline.useStyle(zrUtil.defaults(
// Use color in lineStyle first
lineStyleModel.getLineStyle(),
{
fill: 'none',
stroke: data.getVisual('color'),
lineJoin: 'bevel'
}
));
var smooth = seriesModel.get('smooth');
smooth = getSmooth(seriesModel.get('smooth'));
polyline.setShape({
smooth: smooth,
smoothMonotone: seriesModel.get('smoothMonotone'),
connectNulls: seriesModel.get('connectNulls')
});
if (polygon) {
var stackedOn = data.stackedOn;
var stackedOnSmooth = 0;
polygon.useStyle(zrUtil.defaults(
areaStyleModel.getAreaStyle(),
{
fill: data.getVisual('color'),
opacity: 0.7,
lineJoin: 'bevel'
}
));
if (stackedOn) {
var stackedOnSeries = stackedOn.hostModel;
stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
}
polygon.setShape({
smooth: smooth,
stackedOnSmooth: stackedOnSmooth,
smoothMonotone: seriesModel.get('smoothMonotone'),
connectNulls: seriesModel.get('connectNulls')
});
}
this._data = data;
// Save the coordinate system for transition animation when data changed
this._coordSys = coordSys;
this._stackedOnPoints = stackedOnPoints;
this._points = points;
},
highlight: function (seriesModel, ecModel, api, payload) {
var data = seriesModel.getData();
var dataIndex = queryDataIndex(data, payload);
if (dataIndex != null && dataIndex >= 0) {
var symbol = data.getItemGraphicEl(dataIndex);
if (!symbol) {
// Create a temporary symbol if it is not exists
var pt = data.getItemLayout(dataIndex);
symbol = new Symbol(data, dataIndex, api);
symbol.position = pt;
symbol.setZ(
seriesModel.get('zlevel'),
seriesModel.get('z')
);
symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
symbol.__temp = true;
data.setItemGraphicEl(dataIndex, symbol);
// Stop scale animation
symbol.stopSymbolAnimation(true);
this.group.add(symbol);
}
symbol.highlight();
}
else {
// Highlight whole series
ChartView.prototype.highlight.call(
this, seriesModel, ecModel, api, payload
);
}
},
downplay: function (seriesModel, ecModel, api, payload) {
var data = seriesModel.getData();
var dataIndex = queryDataIndex(data, payload);
if (dataIndex != null && dataIndex >= 0) {
var symbol = data.getItemGraphicEl(dataIndex);
if (symbol) {
if (symbol.__temp) {
data.setItemGraphicEl(dataIndex, null);
this.group.remove(symbol);
}
else {
symbol.downplay();
}
}
}
else {
// Downplay whole series
ChartView.prototype.downplay.call(
this, seriesModel, ecModel, api, payload
);
}
},
/**
* @param {module:zrender/container/Group} group
* @param {Array.<Array.<number>>} points
* @private
*/
_newPolyline: function (points) {
var polyline = this._polyline;
// Remove previous created polyline
if (polyline) {
this._lineGroup.remove(polyline);
}
polyline = new polyHelper.Polyline({
shape: {
points: points
},
silent: true,
z2: 10
});
this._lineGroup.add(polyline);
this._polyline = polyline;
return polyline;
},
/**
* @param {module:zrender/container/Group} group
* @param {Array.<Array.<number>>} stackedOnPoints
* @param {Array.<Array.<number>>} points
* @private
*/
_newPolygon: function (points, stackedOnPoints) {
var polygon = this._polygon;
// Remove previous created polygon
if (polygon) {
this._lineGroup.remove(polygon);
}
polygon = new polyHelper.Polygon({
shape: {
points: points,
stackedOnPoints: stackedOnPoints
},
silent: true
});
this._lineGroup.add(polygon);
this._polygon = polygon;
return polygon;
},
/**
* @private
*/
_getSymbolIgnoreFunc: function (data, coordSys) {
var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
// `getLabelInterval` is provided by echarts/component/axis
if (categoryAxis && categoryAxis.isLabelIgnored) {
return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis);
}
},
/**
* @private
*/
// FIXME Two value axis
_updateAnimation: function (data, stackedOnPoints, coordSys, api) {
var polyline = this._polyline;
var polygon = this._polygon;
var seriesModel = data.hostModel;
var diff = lineAnimationDiff(
this._data, data,
this._stackedOnPoints, stackedOnPoints,
this._coordSys, coordSys
);
polyline.shape.points = diff.current;
graphic.updateProps(polyline, {
shape: {
points: diff.next
}
}, seriesModel);
if (polygon) {
polygon.setShape({
points: diff.current,
stackedOnPoints: diff.stackedOnCurrent
});
graphic.updateProps(polygon, {
shape: {
points: diff.next,
stackedOnPoints: diff.stackedOnNext
}
}, seriesModel);
}
var updatedDataInfo = [];
var diffStatus = diff.status;
for (var i = 0; i < diffStatus.length; i++) {
var cmd = diffStatus[i].cmd;
if (cmd === '=') {
var el = data.getItemGraphicEl(diffStatus[i].idx1);
if (el) {
updatedDataInfo.push({
el: el,
ptIdx: i // Index of points
});
}
}
}
if (polyline.animators && polyline.animators.length) {
polyline.animators[0].during(function () {
for (var i = 0; i < updatedDataInfo.length; i++) {
var el = updatedDataInfo[i].el;
el.attr('position', polyline.shape.points[updatedDataInfo[i].ptIdx]);
}
});
}
},
remove: function (ecModel) {
var group = this.group;
var oldData = this._data;
this._lineGroup.removeAll();
this._symbolDraw.remove(true);
// Remove temporary created elements when highlighting
oldData && oldData.eachItemGraphicEl(function (el, idx) {
if (el.__temp) {
group.remove(el);
oldData.setItemGraphicEl(idx, null);
}
});
this._polyline =
this._polygon =
this._coordSys =
this._points =
this._stackedOnPoints =
this._data = null;
}
});
});

View File

@@ -0,0 +1,209 @@
define(function (require) {
// var arrayDiff = require('zrender/core/arrayDiff');
// 'zrender/core/arrayDiff' has been used before, but it did
// not do well in performance when roam with fixed dataZoom window.
function sign(val) {
return val >= 0 ? 1 : -1;
}
function getStackedOnPoint(coordSys, data, idx) {
var baseAxis = coordSys.getBaseAxis();
var valueAxis = coordSys.getOtherAxis(baseAxis);
var valueStart = baseAxis.onZero
? 0 : valueAxis.scale.getExtent()[0];
var valueDim = valueAxis.dim;
var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
var stackedOnSameSign;
var stackedOn = data.stackedOn;
var val = data.get(valueDim, idx);
// Find first stacked value with same sign
while (stackedOn &&
sign(stackedOn.get(valueDim, idx)) === sign(val)
) {
stackedOnSameSign = stackedOn;
break;
}
var stackedData = [];
stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
stackedData[1 - baseDataOffset] = stackedOnSameSign
? stackedOnSameSign.get(valueDim, idx, true) : valueStart;
return coordSys.dataToPoint(stackedData);
}
// function convertToIntId(newIdList, oldIdList) {
// // Generate int id instead of string id.
// // Compare string maybe slow in score function of arrDiff
// // Assume id in idList are all unique
// var idIndicesMap = {};
// var idx = 0;
// for (var i = 0; i < newIdList.length; i++) {
// idIndicesMap[newIdList[i]] = idx;
// newIdList[i] = idx++;
// }
// for (var i = 0; i < oldIdList.length; i++) {
// var oldId = oldIdList[i];
// // Same with newIdList
// if (idIndicesMap[oldId]) {
// oldIdList[i] = idIndicesMap[oldId];
// }
// else {
// oldIdList[i] = idx++;
// }
// }
// }
function diffData(oldData, newData) {
var diffResult = [];
newData.diff(oldData)
.add(function (idx) {
diffResult.push({cmd: '+', idx: idx});
})
.update(function (newIdx, oldIdx) {
diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx});
})
.remove(function (idx) {
diffResult.push({cmd: '-', idx: idx});
})
.execute();
return diffResult;
}
return function (
oldData, newData,
oldStackedOnPoints, newStackedOnPoints,
oldCoordSys, newCoordSys
) {
var diff = diffData(oldData, newData);
// var newIdList = newData.mapArray(newData.getId);
// var oldIdList = oldData.mapArray(oldData.getId);
// convertToIntId(newIdList, oldIdList);
// // FIXME One data ?
// diff = arrayDiff(oldIdList, newIdList);
var currPoints = [];
var nextPoints = [];
// Points for stacking base line
var currStackedPoints = [];
var nextStackedPoints = [];
var status = [];
var sortedIndices = [];
var rawIndices = [];
var dims = newCoordSys.dimensions;
for (var i = 0; i < diff.length; i++) {
var diffItem = diff[i];
var pointAdded = true;
// FIXME, animation is not so perfect when dataZoom window moves fast
// Which is in case remvoing or add more than one data in the tail or head
switch (diffItem.cmd) {
case '=':
var currentPt = oldData.getItemLayout(diffItem.idx);
var nextPt = newData.getItemLayout(diffItem.idx1);
// If previous data is NaN, use next point directly
if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
currentPt = nextPt.slice();
}
currPoints.push(currentPt);
nextPoints.push(nextPt);
currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
rawIndices.push(newData.getRawIndex(diffItem.idx1));
break;
case '+':
var idx = diffItem.idx;
currPoints.push(
oldCoordSys.dataToPoint([
newData.get(dims[0], idx, true), newData.get(dims[1], idx, true)
])
);
nextPoints.push(newData.getItemLayout(idx).slice());
currStackedPoints.push(
getStackedOnPoint(oldCoordSys, newData, idx)
);
nextStackedPoints.push(newStackedOnPoints[idx]);
rawIndices.push(newData.getRawIndex(idx));
break;
case '-':
var idx = diffItem.idx;
var rawIndex = oldData.getRawIndex(idx);
// Data is replaced. In the case of dynamic data queue
// FIXME FIXME FIXME
if (rawIndex !== idx) {
currPoints.push(oldData.getItemLayout(idx));
nextPoints.push(newCoordSys.dataToPoint([
oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true)
]));
currStackedPoints.push(oldStackedOnPoints[idx]);
nextStackedPoints.push(
getStackedOnPoint(
newCoordSys, oldData, idx
)
);
rawIndices.push(rawIndex);
}
else {
pointAdded = false;
}
}
// Original indices
if (pointAdded) {
status.push(diffItem);
sortedIndices.push(sortedIndices.length);
}
}
// Diff result may be crossed if all items are changed
// Sort by data index
sortedIndices.sort(function (a, b) {
return rawIndices[a] - rawIndices[b];
});
var sortedCurrPoints = [];
var sortedNextPoints = [];
var sortedCurrStackedPoints = [];
var sortedNextStackedPoints = [];
var sortedStatus = [];
for (var i = 0; i < sortedIndices.length; i++) {
var idx = sortedIndices[i];
sortedCurrPoints[i] = currPoints[idx];
sortedNextPoints[i] = nextPoints[idx];
sortedCurrStackedPoints[i] = currStackedPoints[idx];
sortedNextStackedPoints[i] = nextStackedPoints[idx];
sortedStatus[i] = status[idx];
}
return {
current: sortedCurrPoints,
next: sortedNextPoints,
stackedOnCurrent: sortedCurrStackedPoints,
stackedOnNext: sortedNextStackedPoints,
status: sortedStatus
};
};
});

250
vendors/echarts/src/chart/line/poly.js vendored Normal file
View File

@@ -0,0 +1,250 @@
// Poly path support NaN point
define(function (require) {
var Path = require('zrender/graphic/Path');
var vec2 = require('zrender/core/vector');
var vec2Min = vec2.min;
var vec2Max = vec2.max;
var scaleAndAdd = vec2.scaleAndAdd;
var v2Copy = vec2.copy;
// Temporary variable
var v = [];
var cp0 = [];
var cp1 = [];
function isPointNull(p) {
return isNaN(p[0]) || isNaN(p[1]);
}
function drawSegment(
ctx, points, start, segLen, allLen,
dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls
) {
var prevIdx = 0;
var idx = start;
for (var k = 0; k < segLen; k++) {
var p = points[idx];
if (idx >= allLen || idx < 0) {
break;
}
if (isPointNull(p)) {
if (connectNulls) {
idx += dir;
continue;
}
break;
}
if (idx === start) {
ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
v2Copy(cp0, p);
}
else {
if (smooth > 0) {
var nextIdx = idx + dir;
var nextP = points[nextIdx];
if (connectNulls) {
// Find next point not null
while (nextP && isPointNull(points[nextIdx])) {
nextIdx += dir;
nextP = points[nextIdx];
}
}
var ratioNextSeg = 0.5;
var prevP = points[prevIdx];
var nextP = points[nextIdx];
// Last point
if (!nextP || isPointNull(nextP)) {
v2Copy(cp1, p);
}
else {
// If next data is null in not connect case
if (isPointNull(nextP) && !connectNulls) {
nextP = p;
}
vec2.sub(v, nextP, prevP);
var lenPrevSeg;
var lenNextSeg;
if (smoothMonotone === 'x' || smoothMonotone === 'y') {
var dim = smoothMonotone === 'x' ? 0 : 1;
lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
lenNextSeg = Math.abs(p[dim] - nextP[dim]);
}
else {
lenPrevSeg = vec2.dist(p, prevP);
lenNextSeg = vec2.dist(p, nextP);
}
// Use ratio of seg length
ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg));
}
// Smooth constraint
vec2Min(cp0, cp0, smoothMax);
vec2Max(cp0, cp0, smoothMin);
vec2Min(cp1, cp1, smoothMax);
vec2Max(cp1, cp1, smoothMin);
ctx.bezierCurveTo(
cp0[0], cp0[1],
cp1[0], cp1[1],
p[0], p[1]
);
// cp0 of next segment
scaleAndAdd(cp0, p, v, smooth * ratioNextSeg);
}
else {
ctx.lineTo(p[0], p[1]);
}
}
prevIdx = idx;
idx += dir;
}
return k;
}
function getBoundingBox(points, smoothConstraint) {
var ptMin = [Infinity, Infinity];
var ptMax = [-Infinity, -Infinity];
if (smoothConstraint) {
for (var i = 0; i < points.length; i++) {
var pt = points[i];
if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; }
if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; }
if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; }
if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; }
}
}
return {
min: smoothConstraint ? ptMin : ptMax,
max: smoothConstraint ? ptMax : ptMin
};
}
return {
Polyline: Path.extend({
type: 'ec-polyline',
shape: {
points: [],
smooth: 0,
smoothConstraint: true,
smoothMonotone: null,
connectNulls: false
},
style: {
fill: null,
stroke: '#000'
},
buildPath: function (ctx, shape) {
var points = shape.points;
var i = 0;
var len = points.length;
var result = getBoundingBox(points, shape.smoothConstraint);
if (shape.connectNulls) {
// Must remove first and last null values avoid draw error in polygon
for (; len > 0; len--) {
if (!isPointNull(points[len - 1])) {
break;
}
}
for (; i < len; i++) {
if (!isPointNull(points[i])) {
break;
}
}
}
while (i < len) {
i += drawSegment(
ctx, points, i, len, len,
1, result.min, result.max, shape.smooth,
shape.smoothMonotone, shape.connectNulls
) + 1;
}
}
}),
Polygon: Path.extend({
type: 'ec-polygon',
shape: {
points: [],
// Offset between stacked base points and points
stackedOnPoints: [],
smooth: 0,
stackedOnSmooth: 0,
smoothConstraint: true,
smoothMonotone: null,
connectNulls: false
},
buildPath: function (ctx, shape) {
var points = shape.points;
var stackedOnPoints = shape.stackedOnPoints;
var i = 0;
var len = points.length;
var smoothMonotone = shape.smoothMonotone;
var bbox = getBoundingBox(points, shape.smoothConstraint);
var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint);
if (shape.connectNulls) {
// Must remove first and last null values avoid draw error in polygon
for (; len > 0; len--) {
if (!isPointNull(points[len - 1])) {
break;
}
}
for (; i < len; i++) {
if (!isPointNull(points[i])) {
break;
}
}
}
while (i < len) {
var k = drawSegment(
ctx, points, i, len, len,
1, bbox.min, bbox.max, shape.smooth,
smoothMonotone, shape.connectNulls
);
drawSegment(
ctx, stackedOnPoints, i + k - 1, k, len,
-1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth,
smoothMonotone, shape.connectNulls
);
i += k + 1;
ctx.closePath();
}
}
})
};
});

15
vendors/echarts/src/chart/lines.js vendored Normal file
View File

@@ -0,0 +1,15 @@
define(function (require) {
require('./lines/LinesSeries');
require('./lines/LinesView');
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
echarts.registerLayout(
require('./lines/linesLayout')
);
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/seriesColor'), 'lines', 'lineStyle')
);
});

View File

@@ -0,0 +1,117 @@
define(function (require) {
'use strict';
var SeriesModel = require('../../model/Series');
var List = require('../../data/List');
var zrUtil = require('zrender/core/util');
var CoordinateSystem = require('../../CoordinateSystem');
return SeriesModel.extend({
type: 'series.lines',
dependencies: ['grid', 'polar'],
getInitialData: function (option, ecModel) {
var fromDataArr = [];
var toDataArr = [];
var lineDataArr = [];
zrUtil.each(option.data, function (opt) {
fromDataArr.push(opt[0]);
toDataArr.push(opt[1]);
lineDataArr.push(zrUtil.extend(
zrUtil.extend({}, zrUtil.isArray(opt[0]) ? null : opt[0]),
zrUtil.isArray(opt[1]) ? null : opt[1]
));
});
// var coordSys = option.coordinateSystem;
// if (coordSys !== 'cartesian2d' && coordSys !== 'geo') {
// throw new Error('Coordinate system can only be cartesian2d or geo in lines');
// }
// var dimensions = coordSys === 'geo' ? ['lng', 'lat'] : ['x', 'y'];
var coordSys = CoordinateSystem.get(option.coordinateSystem);
if (!coordSys) {
throw new Error('Invalid coordinate system');
}
var dimensions = coordSys.dimensions;
var fromData = new List(dimensions, this);
var toData = new List(dimensions, this);
var lineData = new List(['value'], this);
function geoCoordGetter(item, dim, dataIndex, dimIndex) {
return item.coord && item.coord[dimIndex];
}
fromData.initData(fromDataArr, null, geoCoordGetter);
toData.initData(toDataArr, null, geoCoordGetter);
lineData.initData(lineDataArr);
this.fromData = fromData;
this.toData = toData;
return lineData;
},
formatTooltip: function (dataIndex) {
var fromName = this.fromData.getName(dataIndex);
var toName = this.toData.getName(dataIndex);
return fromName + ' > ' + toName;
},
defaultOption: {
coordinateSystem: 'geo',
zlevel: 0,
z: 2,
legendHoverLink: true,
hoverAnimation: true,
// Cartesian coordinate system
xAxisIndex: 0,
yAxisIndex: 0,
// Geo coordinate system
geoIndex: 0,
// symbol: null,
// symbolSize: 10,
// symbolRotate: null,
effect: {
show: false,
period: 4,
symbol: 'circle',
symbolSize: 3,
// Length of trail, 0 - 1
trailLength: 0.2
// Same with lineStyle.normal.color
// color
},
large: false,
// Available when large is true
largeThreshold: 2000,
label: {
normal: {
show: false,
position: 'end'
// distance: 5,
// formatter: 标签文本格式器同Tooltip.formatter不支持异步回调
}
},
// itemStyle: {
// normal: {
// }
// },
lineStyle: {
normal: {
opacity: 0.5
}
}
}
});
});

View File

@@ -0,0 +1,66 @@
define(function (require) {
var LineDraw = require('../helper/LineDraw');
var EffectLine = require('../helper/EffectLine');
var Line = require('../helper/Line');
require('../../echarts').extendChartView({
type: 'lines',
init: function () {},
render: function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var lineDraw = this._lineDraw;
var hasEffect = seriesModel.get('effect.show');
if (hasEffect !== this._hasEffet) {
if (lineDraw) {
lineDraw.remove();
}
lineDraw = this._lineDraw = new LineDraw(
hasEffect ? EffectLine : Line
);
this._hasEffet = hasEffect;
}
var zlevel = seriesModel.get('zlevel');
var trailLength = seriesModel.get('effect.trailLength');
var zr = api.getZr();
// Avoid the drag cause ghost shadow
// FIXME Better way ?
zr.painter.getLayer(zlevel).clear(true);
// Config layer with motion blur
if (this._lastZlevel != null) {
zr.configLayer(this._lastZlevel, {
motionBlur: false
});
}
if (hasEffect && trailLength) {
zr.configLayer(zlevel, {
motionBlur: true,
lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0)
});
}
this.group.add(lineDraw.group);
lineDraw.updateData(data);
this._lastZlevel = zlevel;
},
updateLayout: function (seriesModel, ecModel, api) {
this._lineDraw.updateLayout();
// Not use motion when dragging or zooming
var zr = api.getZr();
zr.painter.getLayer(this._lastZlevel).clear(true);
},
remove: function (ecModel, api) {
this._lineDraw && this._lineDraw.remove(api, true);
}
});
});

View File

@@ -0,0 +1,32 @@
define(function (require) {
return function (ecModel) {
ecModel.eachSeriesByType('lines', function (seriesModel) {
var coordSys = seriesModel.coordinateSystem;
var fromData = seriesModel.fromData;
var toData = seriesModel.toData;
var lineData = seriesModel.getData();
var dims = coordSys.dimensions;
fromData.each(dims, function (x, y, idx) {
fromData.setItemLayout(idx, coordSys.dataToPoint([x, y]));
});
toData.each(dims, function (x, y, idx) {
toData.setItemLayout(idx, coordSys.dataToPoint([x, y]));
});
lineData.each(function (idx) {
var p1 = fromData.getItemLayout(idx);
var p2 = toData.getItemLayout(idx);
var curveness = lineData.getItemModel(idx).get('lineStyle.normal.curveness');
var cp1;
if (curveness > 0) {
cp1 = [
(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness,
(p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness
];
}
lineData.setItemLayout(idx, [p1, p2, cp1]);
});
});
};
});

34
vendors/echarts/src/chart/map.js vendored Normal file
View File

@@ -0,0 +1,34 @@
define(function (require) {
var echarts = require('../echarts');
require('./map/MapSeries');
require('./map/MapView');
require('../action/geoRoam');
require('../coord/geo/geoCreator');
echarts.registerLayout(require('./map/mapSymbolLayout'));
echarts.registerVisualCoding('chart', require('./map/mapVisual'));
echarts.registerProcessor('statistic', require('./map/mapDataStatistic'));
echarts.registerPreprocessor(require('./map/backwardCompat'));
require('../action/createDataSelectAction')('map', [{
type: 'mapToggleSelect',
event: 'mapselectchanged',
method: 'toggleSelected'
}, {
type: 'mapSelect',
event: 'mapselected',
method: 'select'
}, {
type: 'mapUnSelect',
event: 'mapunselected',
method: 'unSelect'
}]);
});

View File

@@ -0,0 +1,190 @@
define(function (require) {
var List = require('../../data/List');
var SeriesModel = require('../../model/Series');
var zrUtil = require('zrender/core/util');
var completeDimensions = require('../../data/helper/completeDimensions');
var formatUtil = require('../../util/format');
var encodeHTML = formatUtil.encodeHTML;
var addCommas = formatUtil.addCommas;
var dataSelectableMixin = require('../../component/helper/selectableMixin');
var geoCreator = require('../../coord/geo/geoCreator');
var MapSeries = SeriesModel.extend({
type: 'series.map',
/**
* Only first map series of same mapType will drawMap
* @type {boolean}
*/
needsDrawMap: false,
/**
* Group of all map series with same mapType
* @type {boolean}
*/
seriesGroup: [],
init: function (option) {
option = this._fillOption(option, option.map);
this.option = option;
MapSeries.superApply(this, 'init', arguments);
this.updateSelectedMap(option.data);
},
getInitialData: function (option) {
var dimensions = completeDimensions(['value'], option.data || []);
var list = new List(dimensions, this);
list.initData(option.data);
return list;
},
mergeOption: function (newOption) {
if (newOption.data) {
newOption = this._fillOption(newOption, this.option.map);
}
MapSeries.superCall(this, 'mergeOption', newOption);
this.updateSelectedMap(this.option.data);
},
_fillOption: function (option, mapName) {
// Shallow clone
option = zrUtil.extend({}, option);
option.data = geoCreator.getFilledRegions(option.data, mapName);
return option;
},
getRawValue: function (dataIndex) {
// Use value stored in data instead because it is calculated from multiple series
// FIXME Provide all value of multiple series ?
return this._data.get('value', dataIndex);
},
/**
* Get model of region
* @param {string} name
* @return {module:echarts/model/Model}
*/
getRegionModel: function (regionName) {
var data = this.getData();
return data.getItemModel(data.indexOfName(regionName));
},
/**
* Map tooltip formatter
*
* @param {number} dataIndex
*/
formatTooltip: function (dataIndex) {
var data = this._data;
var formattedValue = addCommas(this.getRawValue(dataIndex));
var name = data.getName(dataIndex);
var seriesGroup = this.seriesGroup;
var seriesNames = [];
for (var i = 0; i < seriesGroup.length; i++) {
if (!isNaN(seriesGroup[i].getRawValue(dataIndex))) {
seriesNames.push(
encodeHTML(seriesGroup[i].name)
);
}
}
return seriesNames.join(', ') + '<br />'
+ name + ' : ' + formattedValue;
},
defaultOption: {
// 一级层叠
zlevel: 0,
// 二级层叠
z: 2,
coordinateSystem: 'geo',
// 各省的 map 暂时都用中文
map: 'china',
// 'center' | 'left' | 'right' | 'x%' | {number}
left: 'center',
// 'center' | 'top' | 'bottom' | 'x%' | {number}
top: 'center',
// right
// bottom
// width:
// height // 自适应
// 数值合并方式,默认加和,可选为:
// 'sum' | 'average' | 'max' | 'min'
// mapValueCalculation: 'sum',
// 地图数值计算结果小数精度
// mapValuePrecision: 0,
// 显示图例颜色标识(系列标识的小圆点),图例开启时有效
showLegendSymbol: true,
// 选择模式默认关闭可选singlemultiple
// selectedMode: false,
dataRangeHoverLink: true,
// 是否开启缩放及漫游模式
// roam: false,
// Default on center of map
center: null,
zoom: 1,
scaleLimit: null,
label: {
normal: {
show: false,
textStyle: {
color: '#000'
}
},
emphasis: {
show: true,
textStyle: {
color: 'rgb(100,0,0)'
}
}
},
// scaleLimit: null,
itemStyle: {
normal: {
// color: 各异,
borderWidth: 0.5,
borderColor: '#444',
areaColor: '#eee'
},
// 也是选中样式
emphasis: {
areaColor: 'rgba(255,215, 0, 0.8)'
}
}
},
setZoom: function (zoom) {
this.option.zoom = zoom;
},
setCenter: function (center) {
this.option.center = center;
}
});
zrUtil.mixin(MapSeries, dataSelectableMixin);
return MapSeries;
});

132
vendors/echarts/src/chart/map/MapView.js vendored Normal file
View File

@@ -0,0 +1,132 @@
define(function (require) {
// var zrUtil = require('zrender/core/util');
var graphic = require('../../util/graphic');
var MapDraw = require('../../component/helper/MapDraw');
require('../../echarts').extendChartView({
type: 'map',
render: function (mapModel, ecModel, api, payload) {
// Not render if it is an toggleSelect action from self
if (payload && payload.type === 'mapToggleSelect'
&& payload.from === this.uid
) {
return;
}
var group = this.group;
group.removeAll();
// Not update map if it is an roam action from self
if (!(payload && payload.type === 'geoRoam'
&& payload.component === 'series'
&& payload.name === mapModel.name)) {
if (mapModel.needsDrawMap) {
var mapDraw = this._mapDraw || new MapDraw(api, true);
group.add(mapDraw.group);
mapDraw.draw(mapModel, ecModel, api, this, payload);
this._mapDraw = mapDraw;
}
else {
// Remove drawed map
this._mapDraw && this._mapDraw.remove();
this._mapDraw = null;
}
}
else {
var mapDraw = this._mapDraw;
mapDraw && group.add(mapDraw.group);
}
mapModel.get('showLegendSymbol') && ecModel.getComponent('legend')
&& this._renderSymbols(mapModel, ecModel, api);
},
remove: function () {
this._mapDraw && this._mapDraw.remove();
this._mapDraw = null;
this.group.removeAll();
},
_renderSymbols: function (mapModel, ecModel, api) {
var data = mapModel.getData();
var group = this.group;
data.each('value', function (value, idx) {
if (isNaN(value)) {
return;
}
var layout = data.getItemLayout(idx);
if (!layout || !layout.point) {
// Not exists in map
return;
}
var point = layout.point;
var offset = layout.offset;
var circle = new graphic.Circle({
style: {
fill: data.getVisual('color')
},
shape: {
cx: point[0] + offset * 9,
cy: point[1],
r: 3
},
silent: true,
z2: 10
});
// First data on the same region
if (!offset) {
var labelText = data.getName(idx);
var itemModel = data.getItemModel(idx);
var labelModel = itemModel.getModel('label.normal');
var hoverLabelModel = itemModel.getModel('label.emphasis');
var textStyleModel = labelModel.getModel('textStyle');
var hoverTextStyleModel = hoverLabelModel.getModel('textStyle');
var polygonGroups = data.getItemGraphicEl(idx);
circle.setStyle({
textPosition: 'bottom'
});
var onEmphasis = function () {
circle.setStyle({
text: hoverLabelModel.get('show') ? labelText : '',
textFill: hoverTextStyleModel.getTextColor(),
textFont: hoverTextStyleModel.getFont()
});
};
var onNormal = function () {
circle.setStyle({
text: labelModel.get('show') ? labelText : '',
textFill: textStyleModel.getTextColor(),
textFont: textStyleModel.getFont()
});
};
polygonGroups.on('mouseover', onEmphasis)
.on('mouseout', onNormal)
.on('emphasis', onEmphasis)
.on('normal', onNormal);
onNormal();
}
group.add(circle);
});
}
});
});

View File

@@ -0,0 +1,86 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var geoProps = [
'x', 'y', 'x2', 'y2', 'width', 'height', 'map', 'roam', 'center', 'zoom', 'scaleLimit', 'label', 'itemStyle'
];
var geoCoordsMap = {};
function createGeoFromMap(mapSeriesOpt) {
var geoOpt = {};
zrUtil.each(geoProps, function (propName) {
if (mapSeriesOpt[propName] != null) {
geoOpt[propName] = mapSeriesOpt[propName];
}
});
return geoOpt;
}
return function (option) {
// Save geoCoord
var mapSeries = [];
zrUtil.each(option.series, function (seriesOpt) {
if (seriesOpt.type === 'map') {
mapSeries.push(seriesOpt);
}
zrUtil.extend(geoCoordsMap, seriesOpt.geoCoord);
});
var newCreatedGeoOptMap = {};
zrUtil.each(mapSeries, function (seriesOpt) {
seriesOpt.map = seriesOpt.map || seriesOpt.mapType;
// Put x, y, width, height, x2, y2 in the top level
zrUtil.defaults(seriesOpt, seriesOpt.mapLocation);
if (seriesOpt.markPoint) {
var markPoint = seriesOpt.markPoint;
// Convert name or geoCoord in markPoint to lng and lat
// For example
// { name: 'xxx', value: 10} Or
// { geoCoord: [lng, lat], value: 10} to
// { name: 'xxx', value: [lng, lat, 10]}
markPoint.data = zrUtil.map(markPoint.data, function (dataOpt) {
if (!zrUtil.isArray(dataOpt.value)) {
var geoCoord;
if (dataOpt.geoCoord) {
geoCoord = dataOpt.geoCoord;
}
else if (dataOpt.name) {
geoCoord = geoCoordsMap[dataOpt.name];
}
var newValue = geoCoord ? [geoCoord[0], geoCoord[1]] : [NaN, NaN];
if (dataOpt.value != null) {
newValue.push(dataOpt.value);
}
dataOpt.value = newValue;
}
return dataOpt;
});
// Convert map series which only has markPoint without data to scatter series
// FIXME
if (!(seriesOpt.data && seriesOpt.data.length)) {
if (!option.geo) {
option.geo = [];
}
else if (!zrUtil.isArray(option.geo)) {
option.geo = [option.geo];
}
// Use same geo if multiple map series has same map type
var geoOpt = newCreatedGeoOptMap[seriesOpt.map];
if (!geoOpt) {
geoOpt = newCreatedGeoOptMap[seriesOpt.map] = createGeoFromMap(seriesOpt);
option.geo.push(geoOpt);
}
var scatterSeries = seriesOpt.markPoint;
scatterSeries.type = option.effect && option.effect.show ? 'effectScatter' : 'scatter';
scatterSeries.coordinateSystem = 'geo';
scatterSeries.geoIndex = zrUtil.indexOf(option.geo, geoOpt);
scatterSeries.name = seriesOpt.name;
option.series.splice(zrUtil.indexOf(option.series, seriesOpt), 1, scatterSeries);
}
}
});
};
});

View File

@@ -0,0 +1,80 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
// FIXME 公用?
/**
* @param {Array.<module:echarts/data/List>} datas
* @param {string} statisticsType 'average' 'sum'
* @inner
*/
function dataStatistics(datas, statisticsType) {
var dataNameMap = {};
var dims = ['value'];
for (var i = 0; i < datas.length; i++) {
datas[i].each(dims, function (value, idx) {
var name = datas[i].getName(idx);
dataNameMap[name] = dataNameMap[name] || [];
if (!isNaN(value)) {
dataNameMap[name].push(value);
}
});
}
return datas[0].map(dims, function (value, idx) {
var name = datas[0].getName(idx);
var sum = 0;
var min = Infinity;
var max = -Infinity;
var len = dataNameMap[name].length;
for (var i = 0; i < len; i++) {
min = Math.min(min, dataNameMap[name][i]);
max = Math.max(max, dataNameMap[name][i]);
sum += dataNameMap[name][i];
}
var result;
if (statisticsType === 'min') {
result = min;
}
else if (statisticsType === 'max') {
result = max;
}
else if (statisticsType === 'average') {
result = sum / len;
}
else {
result = sum;
}
return len === 0 ? NaN : result;
});
}
return function (ecModel) {
var seriesGroupByMapType = {};
ecModel.eachSeriesByType('map', function (seriesModel) {
var mapType = seriesModel.get('map');
seriesGroupByMapType[mapType] = seriesGroupByMapType[mapType] || [];
seriesGroupByMapType[mapType].push(seriesModel);
});
zrUtil.each(seriesGroupByMapType, function (seriesList, mapType) {
var data = dataStatistics(
zrUtil.map(seriesList, function (seriesModel) {
return seriesModel.getData();
}),
seriesList[0].get('mapValueCalculation')
);
seriesList[0].seriesGroup = [];
seriesList[0].setData(data);
// FIXME Put where?
for (var i = 0; i < seriesList.length; i++) {
seriesList[i].seriesGroup = seriesList;
seriesList[i].needsDrawMap = i === 0;
}
});
};
});

View File

@@ -0,0 +1,59 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
return function (ecModel) {
var processedMapType = {};
ecModel.eachSeriesByType('map', function (mapSeries) {
var mapType = mapSeries.get('map');
if (processedMapType[mapType]) {
return;
}
var mapSymbolOffsets = {};
zrUtil.each(mapSeries.seriesGroup, function (subMapSeries) {
var geo = subMapSeries.coordinateSystem;
var data = subMapSeries.getData();
if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) {
data.each('value', function (value, idx) {
var name = data.getName(idx);
var region = geo.getRegion(name);
// No region or no value
// In MapSeries data regions will be filled with NaN
// If they are not in the series.data array.
// So here must validate if value is NaN
if (!region || isNaN(value)) {
return;
}
var offset = mapSymbolOffsets[name] || 0;
var point = geo.dataToPoint(region.center);
mapSymbolOffsets[name] = offset + 1;
data.setItemLayout(idx, {
point: point,
offset: offset
});
});
}
});
// Show label of those region not has legendSymbol(which is offset 0)
var data = mapSeries.getData();
data.each(function (idx) {
var name = data.getName(idx);
var layout = data.getItemLayout(idx) || {};
layout.showLabel = !mapSymbolOffsets[name];
data.setItemLayout(idx, layout);
});
processedMapType[mapType] = true;
});
};
});

View File

@@ -0,0 +1,17 @@
define(function (require) {
return function (ecModel) {
ecModel.eachSeriesByType('map', function (seriesModel) {
var colorList = seriesModel.get('color');
var itemStyleModel = seriesModel.getModel('itemStyle.normal');
var areaColor = itemStyleModel.get('areaColor');
var color = itemStyleModel.get('color')
|| colorList[seriesModel.seriesIndex % colorList.length];
seriesModel.getData().setVisual({
'areaColor': areaColor,
'color': color
});
});
};
});

12
vendors/echarts/src/chart/parallel.js vendored Normal file
View File

@@ -0,0 +1,12 @@
define(function (require) {
var echarts = require('../echarts');
require('../component/parallel');
require('./parallel/ParallelSeries');
require('./parallel/ParallelView');
echarts.registerVisualCoding('chart', require('./parallel/parallelVisual'));
});

View File

@@ -0,0 +1,119 @@
define(function(require) {
var List = require('../../data/List');
var zrUtil = require('zrender/core/util');
var SeriesModel = require('../../model/Series');
return SeriesModel.extend({
type: 'series.parallel',
dependencies: ['parallel'],
getInitialData: function (option, ecModel) {
var parallelModel = ecModel.getComponent(
'parallel', this.get('parallelIndex')
);
var dimensions = parallelModel.dimensions;
var parallelAxisIndices = parallelModel.parallelAxisIndex;
var rawData = option.data;
var dimensionsInfo = zrUtil.map(dimensions, function (dim, index) {
var axisModel = ecModel.getComponent(
'parallelAxis', parallelAxisIndices[index]
);
if (axisModel.get('type') === 'category') {
translateCategoryValue(axisModel, dim, rawData);
return {name: dim, type: 'ordinal'};
}
else {
return dim;
}
});
var list = new List(dimensionsInfo, this);
list.initData(rawData);
return list;
},
/**
* User can get data raw indices on 'axisAreaSelected' event received.
*
* @public
* @param {string} activeState 'active' or 'inactive' or 'normal'
* @return {Array.<number>} Raw indices
*/
getRawIndicesByActiveState: function (activeState) {
var coordSys = this.coordinateSystem;
var data = this.getData();
var indices = [];
coordSys.eachActiveState(data, function (theActiveState, dataIndex) {
if (activeState === theActiveState) {
indices.push(data.getRawIndex(dataIndex));
}
});
return indices;
},
defaultOption: {
zlevel: 0, // 一级层叠
z: 2, // 二级层叠
coordinateSystem: 'parallel',
parallelIndex: 0,
// FIXME 尚无用
label: {
normal: {
show: false
// formatter: 标签文本格式器同Tooltip.formatter不支持异步回调
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式详见TEXTSTYLE
},
emphasis: {
show: false
// formatter: 标签文本格式器同Tooltip.formatter不支持异步回调
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
// 'inside'|'left'|'right'|'top'|'bottom'
// textStyle: null // 默认使用全局文本样式详见TEXTSTYLE
}
},
inactiveOpacity: 0.05,
activeOpacity: 1,
lineStyle: {
normal: {
width: 2,
opacity: 0.45,
type: 'solid'
}
},
// smooth: false
animationEasing: 'linear'
}
});
function translateCategoryValue(axisModel, dim, rawData) {
var axisData = axisModel.get('data');
var numberDim = +dim.replace('dim', '');
if (axisData && axisData.length) {
zrUtil.each(rawData, function (dataItem) {
if (!dataItem) {
return;
}
var index = zrUtil.indexOf(axisData, dataItem[numberDim]);
dataItem[numberDim] = index >= 0 ? index : NaN;
});
}
// FIXME
// 如果没有设置axis data, 应自动算出,或者提示。
}
});

View File

@@ -0,0 +1,200 @@
define(function (require) {
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
var ParallelView = require('../../view/Chart').extend({
type: 'parallel',
init: function () {
/**
* @type {module:zrender/container/Group}
* @private
*/
this._dataGroup = new graphic.Group();
this.group.add(this._dataGroup);
/**
* @type {module:echarts/data/List}
*/
this._data;
},
/**
* @override
*/
render: function (seriesModel, ecModel, api, payload) {
var dataGroup = this._dataGroup;
var data = seriesModel.getData();
var oldData = this._data;
var coordSys = seriesModel.coordinateSystem;
var dimensions = coordSys.dimensions;
data.diff(oldData)
.add(add)
.update(update)
.remove(remove)
.execute();
// Update style
data.eachItemGraphicEl(function (elGroup, idx) {
var itemModel = data.getItemModel(idx);
var lineStyleModel = itemModel.getModel('lineStyle.normal');
elGroup.eachChild(function (child) {
child.useStyle(zrUtil.extend(
lineStyleModel.getLineStyle(),
{
fill: null,
stroke: data.getItemVisual(idx, 'color'),
opacity: data.getItemVisual(idx, 'opacity')
}
));
});
});
// First create
if (!this._data) {
dataGroup.setClipPath(createGridClipShape(
coordSys, seriesModel, function () {
dataGroup.removeClipPath();
}
));
}
this._data = data;
function add(newDataIndex) {
var values = data.getValues(dimensions, newDataIndex);
var elGroup = new graphic.Group();
dataGroup.add(elGroup);
eachAxisPair(
values, dimensions, coordSys,
function (pointPair, pairIndex) {
// FIXME
// init animation
if (pointPair) {
elGroup.add(createEl(pointPair));
}
}
);
data.setItemGraphicEl(newDataIndex, elGroup);
}
function update(newDataIndex, oldDataIndex) {
var values = data.getValues(dimensions, newDataIndex);
var elGroup = oldData.getItemGraphicEl(oldDataIndex);
var newEls = [];
var elGroupIndex = 0;
eachAxisPair(
values, dimensions, coordSys,
function (pointPair, pairIndex) {
var el = elGroup.childAt(elGroupIndex++);
if (pointPair && !el) {
newEls.push(createEl(pointPair));
}
else if (pointPair) {
graphic.updateProps(el, {
shape: {
points: pointPair
}
}, seriesModel, newDataIndex);
}
}
);
// Remove redundent els
for (var i = elGroup.childCount() - 1; i >= elGroupIndex; i--) {
elGroup.remove(elGroup.childAt(i));
}
// Add new els
for (var i = 0, len = newEls.length; i < len; i++) {
elGroup.add(newEls[i]);
}
data.setItemGraphicEl(newDataIndex, elGroup);
}
function remove(oldDataIndex) {
var elGroup = oldData.getItemGraphicEl(oldDataIndex);
dataGroup.remove(elGroup);
}
},
/**
* @override
*/
remove: function () {
this._dataGroup && this._dataGroup.removeAll();
this._data = null;
}
});
function createGridClipShape(coordSys, seriesModel, cb) {
var parallelModel = coordSys.model;
var rect = coordSys.getRect();
var rectEl = new graphic.Rect({
shape: {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
}
});
var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height';
rectEl.setShape(dim, 0);
graphic.initProps(rectEl, {
shape: {
width: rect.width,
height: rect.height
}
}, seriesModel, cb);
return rectEl;
}
function eachAxisPair(values, dimensions, coordSys, cb) {
for (var i = 0, len = dimensions.length - 1; i < len; i++) {
var dimA = dimensions[i];
var dimB = dimensions[i + 1];
var valueA = values[i];
var valueB = values[i + 1];
cb(
(isEmptyValue(valueA, coordSys.getAxis(dimA).type)
|| isEmptyValue(valueB, coordSys.getAxis(dimB).type)
)
? null
: [
coordSys.dataToPoint(valueA, dimA),
coordSys.dataToPoint(valueB, dimB)
],
i
);
}
}
function createEl(pointPair) {
return new graphic.Polyline({
shape: {points: pointPair},
silent: true
});
}
// FIXME
// 公用方法?
function isEmptyValue(val, axisType) {
return axisType === 'category'
? val == null
: (val == null || isNaN(val)); // axisType === 'value'
}
return ParallelView;
});

View File

@@ -0,0 +1,37 @@
define(function (require) {
/**
* @payload
* @property {string} parallelAxisId
* @property {Array.<number>} extent
*/
return function (ecModel, payload) {
ecModel.eachSeriesByType('parallel', function (seriesModel) {
var itemStyleModel = seriesModel.getModel('itemStyle.normal');
var globalColors = ecModel.get('color');
var color = itemStyleModel.get('color')
|| globalColors[seriesModel.seriesIndex % globalColors.length];
var inactiveOpacity = seriesModel.get('inactiveOpacity');
var activeOpacity = seriesModel.get('activeOpacity');
var lineStyle = seriesModel.getModel('lineStyle.normal').getLineStyle();
var coordSys = seriesModel.coordinateSystem;
var data = seriesModel.getData();
var opacityMap = {
normal: lineStyle.opacity,
active: activeOpacity,
inactive: inactiveOpacity
};
coordSys.eachActiveState(data, function (activeState, dataIndex) {
data.setItemVisual(dataIndex, 'opacity', opacityMap[activeState]);
});
data.setVisual('color', color);
});
};
});

34
vendors/echarts/src/chart/pie.js vendored Normal file
View File

@@ -0,0 +1,34 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
require('./pie/PieSeries');
require('./pie/PieView');
require('../action/createDataSelectAction')('pie', [{
type: 'pieToggleSelect',
event: 'pieselectchanged',
method: 'toggleSelected'
}, {
type: 'pieSelect',
event: 'pieselected',
method: 'select'
}, {
type: 'pieUnSelect',
event: 'pieunselected',
method: 'unSelect'
}]);
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/dataColor'), 'pie')
);
echarts.registerLayout(zrUtil.curry(
require('./pie/pieLayout'), 'pie'
));
echarts.registerProcessor(
'filter', zrUtil.curry(require('../processor/dataFilter'), 'pie')
);
});

View File

@@ -0,0 +1,146 @@
define(function(require) {
'use strict';
var List = require('../../data/List');
var zrUtil = require('zrender/core/util');
var modelUtil = require('../../util/model');
var completeDimensions = require('../../data/helper/completeDimensions');
var dataSelectableMixin = require('../../component/helper/selectableMixin');
var PieSeries = require('../../echarts').extendSeriesModel({
type: 'series.pie',
// Overwrite
init: function (option) {
PieSeries.superApply(this, 'init', 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;
};
this.updateSelectedMap(option.data);
this._defaultLabelLine(option);
},
// Overwrite
mergeOption: function (newOption) {
PieSeries.superCall(this, 'mergeOption', newOption);
this.updateSelectedMap(this.option.data);
},
getInitialData: function (option, ecModel) {
var dimensions = completeDimensions(['value'], option.data);
var list = new List(dimensions, this);
list.initData(option.data);
return list;
},
// Overwrite
getDataParams: function (dataIndex) {
var data = this._data;
var params = PieSeries.superCall(this, 'getDataParams', dataIndex);
var sum = data.getSum('value');
// FIXME toFixed?
//
// Percent is 0 if sum is 0
params.percent = !sum ? 0 : +(data.get('value', dataIndex) / sum * 100).toFixed(2);
params.$vars.push('percent');
return params;
},
_defaultLabelLine: function (option) {
// Extend labelLine emphasis
modelUtil.defaultEmphasis(option.labelLine, ['show']);
var labelLineNormalOpt = option.labelLine.normal;
var labelLineEmphasisOpt = option.labelLine.emphasis;
// Not show label line if `label.normal.show = false`
labelLineNormalOpt.show = labelLineNormalOpt.show
&& option.label.normal.show;
labelLineEmphasisOpt.show = labelLineEmphasisOpt.show
&& option.label.emphasis.show;
},
defaultOption: {
zlevel: 0,
z: 2,
legendHoverLink: true,
hoverAnimation: true,
// 默认全局居中
center: ['50%', '50%'],
radius: [0, '75%'],
// 默认顺时针
clockwise: true,
startAngle: 90,
// 最小角度改为0
minAngle: 0,
// 选中是扇区偏移量
selectedOffset: 10,
// If use strategy to avoid label overlapping
avoidLabelOverlap: true,
// 选择模式默认关闭可选singlemultiple
// selectedMode: false,
// 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
// roseType: null,
label: {
normal: {
// If rotate around circle
rotate: false,
show: true,
// 'outer', 'inside', 'center'
position: 'outer'
// formatter: 标签文本格式器同Tooltip.formatter不支持异步回调
// textStyle: null // 默认使用全局文本样式详见TEXTSTYLE
// distance: 当position为inner时有效为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数
},
emphasis: {}
},
// Enabled when label.normal.position is 'outer'
labelLine: {
normal: {
show: true,
// 引导线两段中的第一段长度
length: 15,
// 引导线两段中的第二段长度
length2: 15,
smooth: false,
lineStyle: {
// color: 各异,
width: 1,
type: 'solid'
}
}
},
itemStyle: {
normal: {
// color: 各异,
borderColor: 'rgba(0,0,0,0)',
borderWidth: 1
},
emphasis: {
// color: 各异,
borderColor: 'rgba(0,0,0,0)',
borderWidth: 1
}
},
animationEasing: 'cubicOut',
data: []
}
});
zrUtil.mixin(PieSeries, dataSelectableMixin);
return PieSeries;
});

362
vendors/echarts/src/chart/pie/PieView.js vendored Normal file
View File

@@ -0,0 +1,362 @@
define(function (require) {
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
/**
* @param {module:echarts/model/Series} seriesModel
* @param {boolean} hasAnimation
* @inner
*/
function updateDataSelected(uid, seriesModel, hasAnimation, api) {
var data = seriesModel.getData();
var dataIndex = this.dataIndex;
var name = data.getName(dataIndex);
var selectedOffset = seriesModel.get('selectedOffset');
api.dispatchAction({
type: 'pieToggleSelect',
from: uid,
name: name,
seriesId: seriesModel.id
});
data.each(function (idx) {
toggleItemSelected(
data.getItemGraphicEl(idx),
data.getItemLayout(idx),
seriesModel.isSelected(data.getName(idx)),
selectedOffset,
hasAnimation
);
});
}
/**
* @param {module:zrender/graphic/Sector} el
* @param {Object} layout
* @param {boolean} isSelected
* @param {number} selectedOffset
* @param {boolean} hasAnimation
* @inner
*/
function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) {
var midAngle = (layout.startAngle + layout.endAngle) / 2;
var dx = Math.cos(midAngle);
var dy = Math.sin(midAngle);
var offset = isSelected ? selectedOffset : 0;
var position = [dx * offset, dy * offset];
hasAnimation
// animateTo will stop revious animation like update transition
? el.animate()
.when(200, {
position: position
})
.start('bounceOut')
: el.attr('position', position);
}
/**
* Piece of pie including Sector, Label, LabelLine
* @constructor
* @extends {module:zrender/graphic/Group}
*/
function PiePiece(data, idx) {
graphic.Group.call(this);
var sector = new graphic.Sector({
z2: 2
});
var polyline = new graphic.Polyline();
var text = new graphic.Text();
this.add(sector);
this.add(polyline);
this.add(text);
this.updateData(data, idx, true);
// Hover to change label and labelLine
function onEmphasis() {
polyline.ignore = polyline.hoverIgnore;
text.ignore = text.hoverIgnore;
}
function onNormal() {
polyline.ignore = polyline.normalIgnore;
text.ignore = text.normalIgnore;
}
this.on('emphasis', onEmphasis)
.on('normal', onNormal)
.on('mouseover', onEmphasis)
.on('mouseout', onNormal);
}
var piePieceProto = PiePiece.prototype;
function getLabelStyle(data, idx, state, labelModel, labelPosition) {
var textStyleModel = labelModel.getModel('textStyle');
var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
return {
fill: textStyleModel.getTextColor()
|| (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')),
opacity: data.getItemVisual(idx, 'opacity'),
textFont: textStyleModel.getFont(),
text: zrUtil.retrieve(
data.hostModel.getFormattedLabel(idx, state), data.getName(idx)
)
};
}
piePieceProto.updateData = function (data, idx, firstCreate) {
var sector = this.childAt(0);
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var sectorShape = zrUtil.extend({}, layout);
sectorShape.label = null;
if (firstCreate) {
sector.setShape(sectorShape);
sector.shape.endAngle = layout.startAngle;
graphic.updateProps(sector, {
shape: {
endAngle: layout.endAngle
}
}, seriesModel, idx);
}
else {
graphic.updateProps(sector, {
shape: sectorShape
}, seriesModel, idx);
}
// Update common style
var itemStyleModel = itemModel.getModel('itemStyle');
var visualColor = data.getItemVisual(idx, 'color');
sector.useStyle(
zrUtil.defaults(
{
fill: visualColor
},
itemStyleModel.getModel('normal').getItemStyle()
)
);
sector.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle();
// Toggle selected
toggleItemSelected(
this,
data.getItemLayout(idx),
itemModel.get('selected'),
seriesModel.get('selectedOffset'),
seriesModel.get('animation')
);
function onEmphasis() {
// Sector may has animation of updating data. Force to move to the last frame
// Or it may stopped on the wrong shape
sector.stopAnimation(true);
sector.animateTo({
shape: {
r: layout.r + 10
}
}, 300, 'elasticOut');
}
function onNormal() {
sector.stopAnimation(true);
sector.animateTo({
shape: {
r: layout.r
}
}, 300, 'elasticOut');
}
sector.off('mouseover').off('mouseout').off('emphasis').off('normal');
if (itemModel.get('hoverAnimation')) {
sector
.on('mouseover', onEmphasis)
.on('mouseout', onNormal)
.on('emphasis', onEmphasis)
.on('normal', onNormal);
}
this._updateLabel(data, idx);
graphic.setHoverStyle(this);
};
piePieceProto._updateLabel = function (data, idx) {
var labelLine = this.childAt(1);
var labelText = this.childAt(2);
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var labelLayout = layout.label;
var visualColor = data.getItemVisual(idx, 'color');
graphic.updateProps(labelLine, {
shape: {
points: labelLayout.linePoints || [
[labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y]
]
}
}, seriesModel, idx);
graphic.updateProps(labelText, {
style: {
x: labelLayout.x,
y: labelLayout.y
}
}, seriesModel, idx);
labelText.attr({
style: {
textVerticalAlign: labelLayout.verticalAlign,
textAlign: labelLayout.textAlign,
textFont: labelLayout.font
},
rotation: labelLayout.rotation,
origin: [labelLayout.x, labelLayout.y],
z2: 10
});
var labelModel = itemModel.getModel('label.normal');
var labelHoverModel = itemModel.getModel('label.emphasis');
var labelLineModel = itemModel.getModel('labelLine.normal');
var labelLineHoverModel = itemModel.getModel('labelLine.emphasis');
var labelPosition = labelModel.get('position') || labelHoverModel.get('position');
labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel, labelPosition));
labelText.ignore = labelText.normalIgnore = !labelModel.get('show');
labelText.hoverIgnore = !labelHoverModel.get('show');
labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show');
labelLine.hoverIgnore = !labelLineHoverModel.get('show');
// Default use item visual color
labelLine.setStyle({
stroke: visualColor,
opacity: data.getItemVisual(idx, 'opacity')
});
labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle());
labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel, labelPosition);
labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle();
var smooth = labelLineModel.get('smooth');
if (smooth && smooth === true) {
smooth = 0.4;
}
labelLine.setShape({
smooth: smooth
});
};
zrUtil.inherits(PiePiece, graphic.Group);
// Pie view
var Pie = require('../../view/Chart').extend({
type: 'pie',
init: function () {
var sectorGroup = new graphic.Group();
this._sectorGroup = sectorGroup;
},
render: function (seriesModel, ecModel, api, payload) {
if (payload && (payload.from === this.uid)) {
return;
}
var data = seriesModel.getData();
var oldData = this._data;
var group = this.group;
var hasAnimation = ecModel.get('animation');
var isFirstRender = !oldData;
var onSectorClick = zrUtil.curry(
updateDataSelected, this.uid, seriesModel, hasAnimation, api
);
var selectedMode = seriesModel.get('selectedMode');
data.diff(oldData)
.add(function (idx) {
var piePiece = new PiePiece(data, idx);
if (isFirstRender) {
piePiece.eachChild(function (child) {
child.stopAnimation(true);
});
}
selectedMode && piePiece.on('click', onSectorClick);
data.setItemGraphicEl(idx, piePiece);
group.add(piePiece);
})
.update(function (newIdx, oldIdx) {
var piePiece = oldData.getItemGraphicEl(oldIdx);
piePiece.updateData(data, newIdx);
piePiece.off('click');
selectedMode && piePiece.on('click', onSectorClick);
group.add(piePiece);
data.setItemGraphicEl(newIdx, piePiece);
})
.remove(function (idx) {
var piePiece = oldData.getItemGraphicEl(idx);
group.remove(piePiece);
})
.execute();
if (hasAnimation && isFirstRender && data.count() > 0) {
var shape = data.getItemLayout(0);
var r = Math.max(api.getWidth(), api.getHeight()) / 2;
var removeClipPath = zrUtil.bind(group.removeClipPath, group);
group.setClipPath(this._createClipPath(
shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel
));
}
this._data = data;
},
_createClipPath: function (
cx, cy, r, startAngle, clockwise, cb, seriesModel
) {
var clipPath = new graphic.Sector({
shape: {
cx: cx,
cy: cy,
r0: 0,
r: r,
startAngle: startAngle,
endAngle: startAngle,
clockwise: clockwise
}
});
graphic.initProps(clipPath, {
shape: {
endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2
}
}, seriesModel, cb);
return clipPath;
}
});
return Pie;
});

View File

@@ -0,0 +1,227 @@
// FIXME emphasis label position is not same with normal label position
define(function (require) {
'use strict';
var textContain = require('zrender/contain/text');
function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) {
list.sort(function (a, b) {
return a.y - b.y;
});
// 压
function shiftDown(start, end, delta, dir) {
for (var j = start; j < end; j++) {
list[j].y += delta;
if (j > start
&& j + 1 < end
&& list[j + 1].y > list[j].y + list[j].height
) {
shiftUp(j, delta / 2);
return;
}
}
shiftUp(end - 1, delta / 2);
}
// 弹
function shiftUp(end, delta) {
for (var j = end; j >= 0; j--) {
list[j].y -= delta;
if (j > 0
&& list[j].y > list[j - 1].y + list[j - 1].height
) {
break;
}
}
}
function changeX(list, isDownList, cx, cy, r, dir) {
var lastDeltaX = dir > 0
? isDownList // 右侧
? Number.MAX_VALUE // 下
: 0 // 上
: isDownList // 左侧
? Number.MAX_VALUE // 下
: 0; // 上
for (var i = 0, l = list.length; i < l; i++) {
// Not change x for center label
if (list[i].position === 'center') {
continue;
}
var deltaY = Math.abs(list[i].y - cy);
var length = list[i].len;
var length2 = list[i].len2;
var deltaX = (deltaY < r + length)
? Math.sqrt(
(r + length + length2) * (r + length + length2)
- deltaY * deltaY
)
: Math.abs(list[i].x - cx);
if (isDownList && deltaX >= lastDeltaX) {
// 右下,左下
deltaX = lastDeltaX - 10;
}
if (!isDownList && deltaX <= lastDeltaX) {
// 右上,左上
deltaX = lastDeltaX + 10;
}
list[i].x = cx + deltaX * dir;
lastDeltaX = deltaX;
}
}
var lastY = 0;
var delta;
var len = list.length;
var upList = [];
var downList = [];
for (var i = 0; i < len; i++) {
delta = list[i].y - lastY;
if (delta < 0) {
shiftDown(i, len, -delta, dir);
}
lastY = list[i].y + list[i].height;
}
if (viewHeight - lastY < 0) {
shiftUp(len - 1, lastY - viewHeight);
}
for (var i = 0; i < len; i++) {
if (list[i].y >= cy) {
downList.push(list[i]);
}
else {
upList.push(list[i]);
}
}
changeX(upList, false, cx, cy, r, dir);
changeX(downList, true, cx, cy, r, dir);
}
function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) {
var leftList = [];
var rightList = [];
for (var i = 0; i < labelLayoutList.length; i++) {
if (labelLayoutList[i].x < cx) {
leftList.push(labelLayoutList[i]);
}
else {
rightList.push(labelLayoutList[i]);
}
}
adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight);
adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight);
for (var i = 0; i < labelLayoutList.length; i++) {
var linePoints = labelLayoutList[i].linePoints;
if (linePoints) {
var dist = linePoints[1][0] - linePoints[2][0];
if (labelLayoutList[i].x < cx) {
linePoints[2][0] = labelLayoutList[i].x + 3;
}
else {
linePoints[2][0] = labelLayoutList[i].x - 3;
}
linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
linePoints[1][0] = linePoints[2][0] + dist;
}
}
}
return function (seriesModel, r, viewWidth, viewHeight) {
var data = seriesModel.getData();
var labelLayoutList = [];
var cx;
var cy;
var hasLabelRotate = false;
data.each(function (idx) {
var layout = data.getItemLayout(idx);
var itemModel = data.getItemModel(idx);
var labelModel = itemModel.getModel('label.normal');
// Use position in normal or emphasis
var labelPosition = labelModel.get('position') || itemModel.get('label.emphasis.position');
var labelLineModel = itemModel.getModel('labelLine.normal');
var labelLineLen = labelLineModel.get('length');
var labelLineLen2 = labelLineModel.get('length2');
var midAngle = (layout.startAngle + layout.endAngle) / 2;
var dx = Math.cos(midAngle);
var dy = Math.sin(midAngle);
var textX;
var textY;
var linePoints;
var textAlign;
cx = layout.cx;
cy = layout.cy;
var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
if (labelPosition === 'center') {
textX = layout.cx;
textY = layout.cy;
textAlign = 'center';
}
else {
var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx;
var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy;
textX = x1 + dx * 3;
textY = y1 + dy * 3;
if (!isLabelInside) {
// For roseType
var x2 = x1 + dx * (labelLineLen + r - layout.r);
var y2 = y1 + dy * (labelLineLen + r - layout.r);
var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
var y3 = y2;
textX = x3 + (dx < 0 ? -5 : 5);
textY = y3;
linePoints = [[x1, y1], [x2, y2], [x3, y3]];
}
textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right');
}
var font = labelModel.getModel('textStyle').getFont();
var labelRotate = labelModel.get('rotate')
? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0;
var text = seriesModel.getFormattedLabel(idx, 'normal')
|| data.getName(idx);
var textRect = textContain.getBoundingRect(
text, font, textAlign, 'top'
);
hasLabelRotate = !!labelRotate;
layout.label = {
x: textX,
y: textY,
position: labelPosition,
height: textRect.height,
len: labelLineLen,
len2: labelLineLen2,
linePoints: linePoints,
textAlign: textAlign,
verticalAlign: 'middle',
font: font,
rotation: labelRotate
};
// Not layout the inside label
if (!isLabelInside) {
labelLayoutList.push(layout.label);
}
});
if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) {
avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight);
}
};
});

View File

@@ -0,0 +1,123 @@
// TODO minAngle
define(function (require) {
var numberUtil = require('../../util/number');
var parsePercent = numberUtil.parsePercent;
var labelLayout = require('./labelLayout');
var zrUtil = require('zrender/core/util');
var PI2 = Math.PI * 2;
var RADIAN = Math.PI / 180;
return function (seriesType, ecModel, api) {
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
var center = seriesModel.get('center');
var radius = seriesModel.get('radius');
if (!zrUtil.isArray(radius)) {
radius = [0, radius];
}
if (!zrUtil.isArray(center)) {
center = [center, center];
}
var width = api.getWidth();
var height = api.getHeight();
var size = Math.min(width, height);
var cx = parsePercent(center[0], width);
var cy = parsePercent(center[1], height);
var r0 = parsePercent(radius[0], size / 2);
var r = parsePercent(radius[1], size / 2);
var data = seriesModel.getData();
var startAngle = -seriesModel.get('startAngle') * RADIAN;
var minAngle = seriesModel.get('minAngle') * RADIAN;
var sum = data.getSum('value');
// Sum may be 0
var unitRadian = Math.PI / (sum || data.count()) * 2;
var clockwise = seriesModel.get('clockwise');
var roseType = seriesModel.get('roseType');
// [0...max]
var extent = data.getDataExtent('value');
extent[0] = 0;
// In the case some sector angle is smaller than minAngle
var restAngle = PI2;
var valueSumLargerThanMinAngle = 0;
var currentAngle = startAngle;
var dir = clockwise ? 1 : -1;
data.each('value', function (value, idx) {
var angle;
// FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
if (roseType !== 'area') {
angle = sum === 0 ? unitRadian : (value * unitRadian);
}
else {
angle = PI2 / (data.count() || 1);
}
if (angle < minAngle) {
angle = minAngle;
restAngle -= minAngle;
}
else {
valueSumLargerThanMinAngle += value;
}
var endAngle = currentAngle + dir * angle;
data.setItemLayout(idx, {
angle: angle,
startAngle: currentAngle,
endAngle: endAngle,
clockwise: clockwise,
cx: cx,
cy: cy,
r0: r0,
r: roseType
? numberUtil.linearMap(value, extent, [r0, r])
: r
});
currentAngle = endAngle;
}, true);
// Some sector is constrained by minAngle
// Rest sectors needs recalculate angle
if (restAngle < PI2) {
// Average the angle if rest angle is not enough after all angles is
// Constrained by minAngle
if (restAngle <= 1e-3) {
var angle = PI2 / data.count();
data.each(function (idx) {
var layout = data.getItemLayout(idx);
layout.startAngle = startAngle + dir * idx * angle;
layout.endAngle = startAngle + dir * (idx + 1) * angle;
});
}
else {
unitRadian = restAngle / valueSumLargerThanMinAngle;
currentAngle = startAngle;
data.each('value', function (value, idx) {
var layout = data.getItemLayout(idx);
var angle = layout.angle === minAngle
? minAngle : value * unitRadian;
layout.startAngle = currentAngle;
layout.endAngle = currentAngle + dir * angle;
currentAngle += angle;
});
}
}
labelLayout(seriesModel, r, width, height);
});
};
});

25
vendors/echarts/src/chart/radar.js vendored Normal file
View File

@@ -0,0 +1,25 @@
define(function (require) {
var zrUtil = require('zrender/core/util');
var echarts = require('../echarts');
// Must use radar component
require('../component/radar');
require('./radar/RadarSeries');
require('./radar/RadarView');
echarts.registerVisualCoding(
'chart', zrUtil.curry(require('../visual/dataColor'), 'radar')
);
echarts.registerVisualCoding('chart', zrUtil.curry(
require('../visual/symbol'), 'radar', 'circle', null
));
echarts.registerLayout(require('./radar/radarLayout'));
echarts.registerProcessor(
'filter', zrUtil.curry(require('../processor/dataFilter'), 'radar')
);
echarts.registerPreprocessor(require('./radar/backwardCompat'));
});

View File

@@ -0,0 +1,75 @@
define(function(require) {
'use strict';
var SeriesModel = require('../../model/Series');
var List = require('../../data/List');
var completeDimensions = require('../../data/helper/completeDimensions');
var zrUtil = require('zrender/core/util');
var RadarSeries = SeriesModel.extend({
type: 'series.radar',
dependencies: ['radar'],
// Overwrite
init: function (option) {
RadarSeries.superApply(this, 'init', 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 data = option.data || [];
var dimensions = completeDimensions(
[], data, [], 'indicator_'
);
var list = new List(dimensions, this);
list.initData(data);
return list;
},
formatTooltip: function (dataIndex) {
var value = this.getRawValue(dataIndex);
var coordSys = this.coordinateSystem;
var indicatorAxes = coordSys.getIndicatorAxes();
return this._data.getName(dataIndex) + '<br />'
+ zrUtil.map(indicatorAxes, function (axis, idx) {
return axis.name + ' : ' + value[idx];
}).join('<br />');
},
defaultOption: {
zlevel: 0,
z: 2,
coordinateSystem: 'radar',
legendHoverLink: true,
radarIndex: 0,
lineStyle: {
normal: {
width: 2,
type: 'solid'
}
},
label: {
normal: {
position: 'top'
}
},
// areaStyle: {
// },
// itemStyle: {}
symbol: 'emptyCircle',
symbolSize: 4
// symbolRotate: null
}
});
return RadarSeries;
});

View File

@@ -0,0 +1,219 @@
define(function (require) {
var graphic = require('../../util/graphic');
var zrUtil = require('zrender/core/util');
var symbolUtil = require('../../util/symbol');
function normalizeSymbolSize(symbolSize) {
if (!zrUtil.isArray(symbolSize)) {
symbolSize = [+symbolSize, +symbolSize];
}
return symbolSize;
}
return require('../../echarts').extendChartView({
type: 'radar',
render: function (seriesModel, ecModel, api) {
var polar = seriesModel.coordinateSystem;
var group = this.group;
var data = seriesModel.getData();
var oldData = this._data;
function createSymbol(data, idx) {
var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
var color = data.getItemVisual(idx, 'color');
if (symbolType === 'none') {
return;
}
var symbolPath = symbolUtil.createSymbol(
symbolType, -0.5, -0.5, 1, 1, color
);
symbolPath.attr({
style: {
strokeNoScale: true
},
z2: 100,
scale: normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'))
});
return symbolPath;
}
function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) {
// Simply rerender all
symbolGroup.removeAll();
for (var i = 0; i < newPoints.length - 1; i++) {
var symbolPath = createSymbol(data, idx);
if (symbolPath) {
symbolPath.__dimIdx = i;
if (oldPoints[i]) {
symbolPath.attr('position', oldPoints[i]);
graphic[isInit ? 'initProps' : 'updateProps'](
symbolPath, {
position: newPoints[i]
}, seriesModel, idx
);
}
else {
symbolPath.attr('position', newPoints[i]);
}
symbolGroup.add(symbolPath);
}
}
}
function getInitialPoints(points) {
return zrUtil.map(points, function (pt) {
return [polar.cx, polar.cy];
});
}
data.diff(oldData)
.add(function (idx) {
var points = data.getItemLayout(idx);
if (!points) {
return;
}
var polygon = new graphic.Polygon();
var polyline = new graphic.Polyline();
var target = {
shape: {
points: points
}
};
polygon.shape.points = getInitialPoints(points);
polyline.shape.points = getInitialPoints(points);
graphic.initProps(polygon, target, seriesModel, idx);
graphic.initProps(polyline, target, seriesModel, idx);
var itemGroup = new graphic.Group();
var symbolGroup = new graphic.Group();
itemGroup.add(polyline);
itemGroup.add(polygon);
itemGroup.add(symbolGroup);
updateSymbols(
polyline.shape.points, points, symbolGroup, data, idx, true
);
data.setItemGraphicEl(idx, itemGroup);
})
.update(function (newIdx, oldIdx) {
var itemGroup = oldData.getItemGraphicEl(oldIdx);
var polyline = itemGroup.childAt(0);
var polygon = itemGroup.childAt(1);
var symbolGroup = itemGroup.childAt(2);
var target = {
shape: {
points: data.getItemLayout(newIdx)
}
};
if (!target.shape.points) {
return;
}
updateSymbols(
polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false
);
graphic.updateProps(polyline, target, seriesModel);
graphic.updateProps(polygon, target, seriesModel);
data.setItemGraphicEl(newIdx, itemGroup);
})
.remove(function (idx) {
group.remove(oldData.getItemGraphicEl(idx));
})
.execute();
data.eachItemGraphicEl(function (itemGroup, idx) {
var itemModel = data.getItemModel(idx);
var polyline = itemGroup.childAt(0);
var polygon = itemGroup.childAt(1);
var symbolGroup = itemGroup.childAt(2);
var color = data.getItemVisual(idx, 'color');
group.add(itemGroup);
polyline.useStyle(
zrUtil.extend(
itemModel.getModel('lineStyle.normal').getLineStyle(),
{
fill: 'none',
stroke: color
}
)
);
polyline.hoverStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle();
var areaStyleModel = itemModel.getModel('areaStyle.normal');
var hoverAreaStyleModel = itemModel.getModel('areaStyle.emphasis');
var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty();
var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty();
hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore;
polygon.ignore = polygonIgnore;
polygon.useStyle(
zrUtil.defaults(
areaStyleModel.getAreaStyle(),
{
fill: color,
opacity: 0.7
}
)
);
polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle();
var itemStyle = itemModel.getModel('itemStyle.normal').getItemStyle(['color']);
var itemHoverStyle = itemModel.getModel('itemStyle.emphasis').getItemStyle();
var labelModel = itemModel.getModel('label.normal');
var labelHoverModel = itemModel.getModel('label.emphasis');
symbolGroup.eachChild(function (symbolPath) {
symbolPath.setStyle(itemStyle);
symbolPath.hoverStyle = zrUtil.clone(itemHoverStyle);
var defaultText = data.get(data.dimensions[symbolPath.__dimIdx], idx);
graphic.setText(symbolPath.style, labelModel, color);
symbolPath.setStyle({
text: labelModel.get('show') ? zrUtil.retrieve(
seriesModel.getFormattedLabel(
idx, 'normal', null, symbolPath.__dimIdx
),
defaultText
) : ''
});
graphic.setText(symbolPath.hoverStyle, labelHoverModel, color);
symbolPath.hoverStyle.text = labelHoverModel.get('show') ? zrUtil.retrieve(
seriesModel.getFormattedLabel(
idx, 'emphasis', null, symbolPath.__dimIdx
),
defaultText
) : '';
});
function onEmphasis() {
polygon.attr('ignore', hoverPolygonIgnore);
}
function onNormal() {
polygon.attr('ignore', polygonIgnore);
}
itemGroup.off('mouseover').off('mouseout').off('normal').off('emphasis');
itemGroup.on('emphasis', onEmphasis)
.on('mouseover', onEmphasis)
.on('normal', onNormal)
.on('mouseout', onNormal);
graphic.setHoverStyle(itemGroup);
});
this._data = data;
},
remove: function () {
this.group.removeAll();
this._data = null;
}
});
});

View File

@@ -0,0 +1,36 @@
// Backward compat for radar chart in 2
define(function (require) {
var zrUtil = require('zrender/core/util');
return function (option) {
var polarOptArr = option.polar;
if (polarOptArr) {
if (!zrUtil.isArray(polarOptArr)) {
polarOptArr = [polarOptArr];
}
var polarNotRadar = [];
zrUtil.each(polarOptArr, function (polarOpt, idx) {
if (polarOpt.indicator) {
if (polarOpt.type && !polarOpt.shape) {
polarOpt.shape = polarOpt.type;
}
option.radar = option.radar || [];
if (!zrUtil.isArray(option.radar)) {
option.radar = [option.radar];
}
option.radar.push(polarOpt);
}
else {
polarNotRadar.push(polarOpt);
}
});
option.polar = polarNotRadar;
}
zrUtil.each(option.series, function (seriesOpt) {
if (seriesOpt.type === 'radar' && seriesOpt.polarIndex) {
seriesOpt.radarIndex = seriesOpt.polarIndex;
}
});
};
});

View File

@@ -0,0 +1,28 @@
define(function (require) {
return function (ecModel, api) {
ecModel.eachSeriesByType('radar', function (seriesModel) {
var data = seriesModel.getData();
var points = [];
var coordSys = seriesModel.coordinateSystem;
if (!coordSys) {
return;
}
function pointsConverter(val, idx) {
points[idx] = points[idx] || [];
points[idx][i] = coordSys.dataToPoint(val, i);
}
for (var i = 0; i < coordSys.getIndicatorAxes().length; i++) {
var dim = data.dimensions[i];
data.each(dim, pointsConverter);
}
data.each(function (idx) {
// Close polygon
points[idx][0] && points[idx].push(points[idx][0].slice());
data.setItemLayout(idx, points[idx]);
});
});
};
});

Some files were not shown because too many files have changed in this diff Show More