(function webpackUniversalModuleDefinition(root, factory) {
|
if(typeof exports === 'object' && typeof module === 'object')
|
module.exports = factory();
|
else if(typeof define === 'function' && define.amd)
|
define([], factory);
|
else if(typeof exports === 'object')
|
exports["echarts"] = factory();
|
else
|
root["echarts"] = factory();
|
})(this, function() {
|
return /******/ (function(modules) { // webpackBootstrap
|
/******/ // The module cache
|
/******/ var installedModules = {};
|
|
/******/ // The require function
|
/******/ function __webpack_require__(moduleId) {
|
|
/******/ // Check if module is in cache
|
/******/ if(installedModules[moduleId])
|
/******/ return installedModules[moduleId].exports;
|
|
/******/ // Create a new module (and put it into the cache)
|
/******/ var module = installedModules[moduleId] = {
|
/******/ exports: {},
|
/******/ id: moduleId,
|
/******/ loaded: false
|
/******/ };
|
|
/******/ // Execute the module function
|
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
/******/ // Flag the module as loaded
|
/******/ module.loaded = true;
|
|
/******/ // Return the exports of the module
|
/******/ return module.exports;
|
/******/ }
|
|
|
/******/ // expose the modules object (__webpack_modules__)
|
/******/ __webpack_require__.m = modules;
|
|
/******/ // expose the module cache
|
/******/ __webpack_require__.c = installedModules;
|
|
/******/ // __webpack_public_path__
|
/******/ __webpack_require__.p = "";
|
|
/******/ // Load entry module and return exports
|
/******/ return __webpack_require__(0);
|
/******/ })
|
/************************************************************************/
|
/******/ ([
|
/* 0 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Export echarts as CommonJS module
|
*/
|
module.exports = __webpack_require__(1);
|
|
__webpack_require__(113);
|
__webpack_require__(139);
|
__webpack_require__(146);
|
__webpack_require__(155);
|
__webpack_require__(312);
|
__webpack_require__(320);
|
__webpack_require__(297);
|
__webpack_require__(314);
|
|
__webpack_require__(313);
|
__webpack_require__(354);
|
|
__webpack_require__(383);
|
__webpack_require__(389);
|
__webpack_require__(392);
|
__webpack_require__(355);
|
__webpack_require__(404);
|
|
__webpack_require__(416);
|
|
/***/ },
|
/* 1 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// Enable DEV mode when using source code without build. which has no __DEV__ variable
|
// In build process 'typeof __DEV__' will be replace with 'boolean'
|
// So this code will be removed or disabled anyway after built.
|
if (false) {
|
// In browser
|
if (typeof window !== 'undefined') {
|
window.__DEV__ = true;
|
}
|
// In node
|
else if (typeof global !== 'undefined') {
|
global.__DEV__ = true;
|
}
|
}
|
|
/*!
|
* ECharts, a javascript interactive chart library.
|
*
|
* Copyright (c) 2015, Baidu Inc.
|
* All rights reserved.
|
*
|
* LICENSE
|
* https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
|
*/
|
|
/**
|
* @module echarts
|
*/
|
|
|
var env = __webpack_require__(2);
|
|
var GlobalModel = __webpack_require__(3);
|
var ExtensionAPI = __webpack_require__(25);
|
var CoordinateSystemManager = __webpack_require__(26);
|
var OptionManager = __webpack_require__(27);
|
|
var ComponentModel = __webpack_require__(19);
|
var SeriesModel = __webpack_require__(28);
|
|
var ComponentView = __webpack_require__(29);
|
var ChartView = __webpack_require__(43);
|
var graphic = __webpack_require__(44);
|
var modelUtil = __webpack_require__(5);
|
var throttle = __webpack_require__(81);
|
|
var zrender = __webpack_require__(82);
|
var zrUtil = __webpack_require__(4);
|
var colorTool = __webpack_require__(39);
|
var Eventful = __webpack_require__(33);
|
var timsort = __webpack_require__(86);
|
|
var each = zrUtil.each;
|
var parseClassType = ComponentModel.parseClassType;
|
|
var PRIORITY_PROCESSOR_FILTER = 1000;
|
var PRIORITY_PROCESSOR_STATISTIC = 5000;
|
|
|
var PRIORITY_VISUAL_LAYOUT = 1000;
|
var PRIORITY_VISUAL_GLOBAL = 2000;
|
var PRIORITY_VISUAL_CHART = 3000;
|
var PRIORITY_VISUAL_COMPONENT = 4000;
|
// FIXME
|
// necessary?
|
var PRIORITY_VISUAL_BRUSH = 5000;
|
|
// Main process have three entries: `setOption`, `dispatchAction` and `resize`,
|
// where they must not be invoked nestedly, except the only case: invoke
|
// dispatchAction with updateMethod "none" in main process.
|
// This flag is used to carry out this rule.
|
// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
|
var IN_MAIN_PROCESS = '__flagInMainProcess';
|
var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
|
var OPTION_UPDATED = '__optionUpdated';
|
var ACTION_REG = /^[a-zA-Z0-9_]+$/;
|
|
function createRegisterEventWithLowercaseName(method) {
|
return function (eventName, handler, context) {
|
// Event name is all lowercase
|
eventName = eventName && eventName.toLowerCase();
|
Eventful.prototype[method].call(this, eventName, handler, context);
|
};
|
}
|
|
/**
|
* @module echarts~MessageCenter
|
*/
|
function MessageCenter() {
|
Eventful.call(this);
|
}
|
MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
|
MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
|
MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
|
zrUtil.mixin(MessageCenter, Eventful);
|
|
/**
|
* @module echarts~ECharts
|
*/
|
function ECharts(dom, theme, opts) {
|
opts = opts || {};
|
|
// Get theme by name
|
if (typeof theme === 'string') {
|
theme = themeStorage[theme];
|
}
|
|
/**
|
* @type {string}
|
*/
|
this.id;
|
/**
|
* Group id
|
* @type {string}
|
*/
|
this.group;
|
/**
|
* @type {HTMLDomElement}
|
* @private
|
*/
|
this._dom = dom;
|
/**
|
* @type {module:zrender/ZRender}
|
* @private
|
*/
|
var zr = this._zr = zrender.init(dom, {
|
renderer: opts.renderer || 'canvas',
|
devicePixelRatio: opts.devicePixelRatio,
|
width: opts.width,
|
height: opts.height
|
});
|
|
/**
|
* Expect 60 pfs.
|
* @type {Function}
|
* @private
|
*/
|
this._throttledZrFlush = throttle.throttle(zrUtil.bind(zr.flush, zr), 17);
|
|
/**
|
* @type {Object}
|
* @private
|
*/
|
this._theme = zrUtil.clone(theme);
|
|
/**
|
* @type {Array.<module:echarts/view/Chart>}
|
* @private
|
*/
|
this._chartsViews = [];
|
|
/**
|
* @type {Object.<string, module:echarts/view/Chart>}
|
* @private
|
*/
|
this._chartsMap = {};
|
|
/**
|
* @type {Array.<module:echarts/view/Component>}
|
* @private
|
*/
|
this._componentsViews = [];
|
|
/**
|
* @type {Object.<string, module:echarts/view/Component>}
|
* @private
|
*/
|
this._componentsMap = {};
|
|
/**
|
* @type {module:echarts/CoordinateSystem}
|
* @private
|
*/
|
this._coordSysMgr = new CoordinateSystemManager();
|
|
/**
|
* @type {module:echarts/ExtensionAPI}
|
* @private
|
*/
|
this._api = createExtensionAPI(this);
|
|
Eventful.call(this);
|
|
/**
|
* @type {module:echarts~MessageCenter}
|
* @private
|
*/
|
this._messageCenter = new MessageCenter();
|
|
// Init mouse events
|
this._initEvents();
|
|
// In case some people write `window.onresize = chart.resize`
|
this.resize = zrUtil.bind(this.resize, this);
|
|
// Can't dispatch action during rendering procedure
|
this._pendingActions = [];
|
// Sort on demand
|
function prioritySortFunc(a, b) {
|
return a.prio - b.prio;
|
}
|
timsort(visualFuncs, prioritySortFunc);
|
timsort(dataProcessorFuncs, prioritySortFunc);
|
|
zr.animation.on('frame', this._onframe, this);
|
|
// ECharts instance can be used as value.
|
zrUtil.setAsPrimitive(this);
|
}
|
|
var echartsProto = ECharts.prototype;
|
|
echartsProto._onframe = function () {
|
// Lazy update
|
if (this[OPTION_UPDATED]) {
|
var silent = this[OPTION_UPDATED].silent;
|
|
this[IN_MAIN_PROCESS] = true;
|
|
updateMethods.prepareAndUpdate.call(this);
|
|
this[IN_MAIN_PROCESS] = false;
|
|
this[OPTION_UPDATED] = false;
|
|
flushPendingActions.call(this, silent);
|
|
triggerUpdatedEvent.call(this, silent);
|
}
|
};
|
/**
|
* @return {HTMLDomElement}
|
*/
|
echartsProto.getDom = function () {
|
return this._dom;
|
};
|
|
/**
|
* @return {module:zrender~ZRender}
|
*/
|
echartsProto.getZr = function () {
|
return this._zr;
|
};
|
|
/**
|
* Usage:
|
* chart.setOption(option, notMerge, lazyUpdate);
|
* chart.setOption(option, {
|
* notMerge: ...,
|
* lazyUpdate: ...,
|
* silent: ...
|
* });
|
*
|
* @param {Object} option
|
* @param {Object|boolean} [opts] opts or notMerge.
|
* @param {boolean} [opts.notMerge=false]
|
* @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
|
*/
|
echartsProto.setOption = function (option, notMerge, lazyUpdate) {
|
if (true) {
|
zrUtil.assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
|
}
|
|
var silent;
|
if (zrUtil.isObject(notMerge)) {
|
lazyUpdate = notMerge.lazyUpdate;
|
silent = notMerge.silent;
|
notMerge = notMerge.notMerge;
|
}
|
|
this[IN_MAIN_PROCESS] = true;
|
|
if (!this._model || notMerge) {
|
var optionManager = new OptionManager(this._api);
|
var theme = this._theme;
|
var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);
|
ecModel.init(null, null, theme, optionManager);
|
}
|
|
// FIXME
|
// ugly
|
this.__lastOnlyGraphic = !!(option && option.graphic);
|
zrUtil.each(option, function (o, mainType) {
|
mainType !== 'graphic' && (this.__lastOnlyGraphic = false);
|
}, this);
|
|
this._model.setOption(option, optionPreprocessorFuncs, this.__lastOnlyGraphic);
|
|
if (lazyUpdate) {
|
this[OPTION_UPDATED] = {silent: silent};
|
this[IN_MAIN_PROCESS] = false;
|
}
|
else {
|
updateMethods.prepareAndUpdate.call(this);
|
// Ensure zr refresh sychronously, and then pixel in canvas can be
|
// fetched after `setOption`.
|
this._zr.flush();
|
|
this[OPTION_UPDATED] = false;
|
this[IN_MAIN_PROCESS] = false;
|
|
flushPendingActions.call(this, silent);
|
triggerUpdatedEvent.call(this, silent);
|
}
|
};
|
|
/**
|
* @DEPRECATED
|
*/
|
echartsProto.setTheme = function () {
|
console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
|
};
|
|
/**
|
* @return {module:echarts/model/Global}
|
*/
|
echartsProto.getModel = function () {
|
return this._model;
|
};
|
|
/**
|
* @return {Object}
|
*/
|
echartsProto.getOption = function () {
|
return this._model && this._model.getOption();
|
};
|
|
/**
|
* @return {number}
|
*/
|
echartsProto.getWidth = function () {
|
return this._zr.getWidth();
|
};
|
|
/**
|
* @return {number}
|
*/
|
echartsProto.getHeight = function () {
|
return this._zr.getHeight();
|
};
|
|
/**
|
* @return {number}
|
*/
|
echartsProto.getDevicePixelRatio = function () {
|
return this._zr.painter.dpr || window.devicePixelRatio || 1;
|
};
|
|
/**
|
* Get canvas which has all thing rendered
|
* @param {Object} opts
|
* @param {string} [opts.backgroundColor]
|
*/
|
echartsProto.getRenderedCanvas = function (opts) {
|
if (!env.canvasSupported) {
|
return;
|
}
|
opts = opts || {};
|
opts.pixelRatio = opts.pixelRatio || 1;
|
opts.backgroundColor = opts.backgroundColor
|
|| this._model.get('backgroundColor');
|
var zr = this._zr;
|
var list = zr.storage.getDisplayList();
|
// Stop animations
|
zrUtil.each(list, function (el) {
|
el.stopAnimation(true);
|
});
|
return zr.painter.getRenderedCanvas(opts);
|
};
|
/**
|
* @return {string}
|
* @param {Object} opts
|
* @param {string} [opts.type='png']
|
* @param {string} [opts.pixelRatio=1]
|
* @param {string} [opts.backgroundColor]
|
* @param {string} [opts.excludeComponents]
|
*/
|
echartsProto.getDataURL = function (opts) {
|
opts = opts || {};
|
var excludeComponents = opts.excludeComponents;
|
var ecModel = this._model;
|
var excludesComponentViews = [];
|
var self = this;
|
|
each(excludeComponents, function (componentType) {
|
ecModel.eachComponent({
|
mainType: componentType
|
}, function (component) {
|
var view = self._componentsMap[component.__viewId];
|
if (!view.group.ignore) {
|
excludesComponentViews.push(view);
|
view.group.ignore = true;
|
}
|
});
|
});
|
|
var url = this.getRenderedCanvas(opts).toDataURL(
|
'image/' + (opts && opts.type || 'png')
|
);
|
|
each(excludesComponentViews, function (view) {
|
view.group.ignore = false;
|
});
|
return url;
|
};
|
|
|
/**
|
* @return {string}
|
* @param {Object} opts
|
* @param {string} [opts.type='png']
|
* @param {string} [opts.pixelRatio=1]
|
* @param {string} [opts.backgroundColor]
|
*/
|
echartsProto.getConnectedDataURL = function (opts) {
|
if (!env.canvasSupported) {
|
return;
|
}
|
var groupId = this.group;
|
var mathMin = Math.min;
|
var mathMax = Math.max;
|
var MAX_NUMBER = Infinity;
|
if (connectedGroups[groupId]) {
|
var left = MAX_NUMBER;
|
var top = MAX_NUMBER;
|
var right = -MAX_NUMBER;
|
var bottom = -MAX_NUMBER;
|
var canvasList = [];
|
var dpr = (opts && opts.pixelRatio) || 1;
|
|
zrUtil.each(instances, function (chart, id) {
|
if (chart.group === groupId) {
|
var canvas = chart.getRenderedCanvas(
|
zrUtil.clone(opts)
|
);
|
var boundingRect = chart.getDom().getBoundingClientRect();
|
left = mathMin(boundingRect.left, left);
|
top = mathMin(boundingRect.top, top);
|
right = mathMax(boundingRect.right, right);
|
bottom = mathMax(boundingRect.bottom, bottom);
|
canvasList.push({
|
dom: canvas,
|
left: boundingRect.left,
|
top: boundingRect.top
|
});
|
}
|
});
|
|
left *= dpr;
|
top *= dpr;
|
right *= dpr;
|
bottom *= dpr;
|
var width = right - left;
|
var height = bottom - top;
|
var targetCanvas = zrUtil.createCanvas();
|
targetCanvas.width = width;
|
targetCanvas.height = height;
|
var zr = zrender.init(targetCanvas);
|
|
each(canvasList, function (item) {
|
var img = new graphic.Image({
|
style: {
|
x: item.left * dpr - left,
|
y: item.top * dpr - top,
|
image: item.dom
|
}
|
});
|
zr.add(img);
|
});
|
zr.refreshImmediately();
|
|
return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
|
}
|
else {
|
return this.getDataURL(opts);
|
}
|
};
|
|
/**
|
* Convert from logical coordinate system to pixel coordinate system.
|
* See CoordinateSystem#convertToPixel.
|
* @param {string|Object} finder
|
* If string, e.g., 'geo', means {geoIndex: 0}.
|
* If Object, could contain some of these properties below:
|
* {
|
* seriesIndex / seriesId / seriesName,
|
* geoIndex / geoId, geoName,
|
* bmapIndex / bmapId / bmapName,
|
* xAxisIndex / xAxisId / xAxisName,
|
* yAxisIndex / yAxisId / yAxisName,
|
* gridIndex / gridId / gridName,
|
* ... (can be extended)
|
* }
|
* @param {Array|number} value
|
* @return {Array|number} result
|
*/
|
echartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');
|
|
/**
|
* Convert from pixel coordinate system to logical coordinate system.
|
* See CoordinateSystem#convertFromPixel.
|
* @param {string|Object} finder
|
* If string, e.g., 'geo', means {geoIndex: 0}.
|
* If Object, could contain some of these properties below:
|
* {
|
* seriesIndex / seriesId / seriesName,
|
* geoIndex / geoId / geoName,
|
* bmapIndex / bmapId / bmapName,
|
* xAxisIndex / xAxisId / xAxisName,
|
* yAxisIndex / yAxisId / yAxisName
|
* gridIndex / gridId / gridName,
|
* ... (can be extended)
|
* }
|
* @param {Array|number} value
|
* @return {Array|number} result
|
*/
|
echartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');
|
|
function doConvertPixel(methodName, finder, value) {
|
var ecModel = this._model;
|
var coordSysList = this._coordSysMgr.getCoordinateSystems();
|
var result;
|
|
finder = modelUtil.parseFinder(ecModel, finder);
|
|
for (var i = 0; i < coordSysList.length; i++) {
|
var coordSys = coordSysList[i];
|
if (coordSys[methodName]
|
&& (result = coordSys[methodName](ecModel, finder, value)) != null
|
) {
|
return result;
|
}
|
}
|
|
if (true) {
|
console.warn(
|
'No coordinate system that supports ' + methodName + ' found by the given finder.'
|
);
|
}
|
}
|
|
/**
|
* Is the specified coordinate systems or components contain the given pixel point.
|
* @param {string|Object} finder
|
* If string, e.g., 'geo', means {geoIndex: 0}.
|
* If Object, could contain some of these properties below:
|
* {
|
* seriesIndex / seriesId / seriesName,
|
* geoIndex / geoId / geoName,
|
* bmapIndex / bmapId / bmapName,
|
* xAxisIndex / xAxisId / xAxisName,
|
* yAxisIndex / yAxisId / yAxisName,
|
* gridIndex / gridId / gridName,
|
* ... (can be extended)
|
* }
|
* @param {Array|number} value
|
* @return {boolean} result
|
*/
|
echartsProto.containPixel = function (finder, value) {
|
var ecModel = this._model;
|
var result;
|
|
finder = modelUtil.parseFinder(ecModel, finder);
|
|
zrUtil.each(finder, function (models, key) {
|
key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {
|
var coordSys = model.coordinateSystem;
|
if (coordSys && coordSys.containPoint) {
|
result |= !!coordSys.containPoint(value);
|
}
|
else if (key === 'seriesModels') {
|
var view = this._chartsMap[model.__viewId];
|
if (view && view.containPoint) {
|
result |= view.containPoint(value, model);
|
}
|
else {
|
if (true) {
|
console.warn(key + ': ' + (view
|
? 'The found component do not support containPoint.'
|
: 'No view mapping to the found component.'
|
));
|
}
|
}
|
}
|
else {
|
if (true) {
|
console.warn(key + ': containPoint is not supported');
|
}
|
}
|
}, this);
|
}, this);
|
|
return !!result;
|
};
|
|
/**
|
* Get visual from series or data.
|
* @param {string|Object} finder
|
* If string, e.g., 'series', means {seriesIndex: 0}.
|
* If Object, could contain some of these properties below:
|
* {
|
* seriesIndex / seriesId / seriesName,
|
* dataIndex / dataIndexInside
|
* }
|
* If dataIndex is not specified, series visual will be fetched,
|
* but not data item visual.
|
* If all of seriesIndex, seriesId, seriesName are not specified,
|
* visual will be fetched from first series.
|
* @param {string} visualType 'color', 'symbol', 'symbolSize'
|
*/
|
echartsProto.getVisual = function (finder, visualType) {
|
var ecModel = this._model;
|
|
finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});
|
|
var seriesModel = finder.seriesModel;
|
|
if (true) {
|
if (!seriesModel) {
|
console.warn('There is no specified seires model');
|
}
|
}
|
|
var data = seriesModel.getData();
|
|
var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
|
? finder.dataIndexInside
|
: finder.hasOwnProperty('dataIndex')
|
? data.indexOfRawIndex(finder.dataIndex)
|
: null;
|
|
return dataIndexInside != null
|
? data.getItemVisual(dataIndexInside, visualType)
|
: data.getVisual(visualType);
|
};
|
|
/**
|
* Get view of corresponding component model
|
* @param {module:echarts/model/Component} componentModel
|
* @return {module:echarts/view/Component}
|
*/
|
echartsProto.getViewOfComponentModel = function (componentModel) {
|
return this._componentsMap[componentModel.__viewId];
|
};
|
|
/**
|
* Get view of corresponding series model
|
* @param {module:echarts/model/Series} seriesModel
|
* @return {module:echarts/view/Chart}
|
*/
|
echartsProto.getViewOfSeriesModel = function (seriesModel) {
|
return this._chartsMap[seriesModel.__viewId];
|
};
|
|
|
var updateMethods = {
|
|
/**
|
* @param {Object} payload
|
* @private
|
*/
|
update: function (payload) {
|
// console.profile && console.profile('update');
|
|
var ecModel = this._model;
|
var api = this._api;
|
var coordSysMgr = this._coordSysMgr;
|
var zr = this._zr;
|
// update before setOption
|
if (!ecModel) {
|
return;
|
}
|
|
// Fixme First time update ?
|
ecModel.restoreData();
|
|
// TODO
|
// Save total ecModel here for undo/redo (after restoring data and before processing data).
|
// Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
|
|
// Create new coordinate system each update
|
// In LineView may save the old coordinate system and use it to get the orignal point
|
coordSysMgr.create(this._model, this._api);
|
|
processData.call(this, ecModel, api);
|
|
stackSeriesData.call(this, ecModel);
|
|
coordSysMgr.update(ecModel, api);
|
|
doVisualEncoding.call(this, ecModel, payload);
|
|
doRender.call(this, ecModel, payload);
|
|
// Set background
|
var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
|
|
var painter = zr.painter;
|
// TODO all use clearColor ?
|
if (painter.isSingleCanvas && painter.isSingleCanvas()) {
|
zr.configLayer(0, {
|
clearColor: backgroundColor
|
});
|
}
|
else {
|
// In IE8
|
if (!env.canvasSupported) {
|
var colorArr = colorTool.parse(backgroundColor);
|
backgroundColor = colorTool.stringify(colorArr, 'rgb');
|
if (colorArr[3] === 0) {
|
backgroundColor = 'transparent';
|
}
|
}
|
if (backgroundColor.colorStops || backgroundColor.image) {
|
// Gradient background
|
// FIXME Fixed layer?
|
zr.configLayer(0, {
|
clearColor: backgroundColor
|
});
|
this[HAS_GRADIENT_OR_PATTERN_BG] = true;
|
|
this._dom.style.background = 'transparent';
|
}
|
else {
|
if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
|
zr.configLayer(0, {
|
clearColor: null
|
});
|
}
|
this[HAS_GRADIENT_OR_PATTERN_BG] = false;
|
|
this._dom.style.background = backgroundColor;
|
}
|
}
|
|
each(postUpdateFuncs, function (func) {
|
func(ecModel, api);
|
});
|
|
// console.profile && console.profileEnd('update');
|
},
|
|
/**
|
* @param {Object} payload
|
* @private
|
*/
|
updateView: function (payload) {
|
var ecModel = this._model;
|
|
// update before setOption
|
if (!ecModel) {
|
return;
|
}
|
|
ecModel.eachSeries(function (seriesModel) {
|
seriesModel.getData().clearAllVisual();
|
});
|
|
doVisualEncoding.call(this, ecModel, payload);
|
|
invokeUpdateMethod.call(this, 'updateView', ecModel, payload);
|
},
|
|
/**
|
* @param {Object} payload
|
* @private
|
*/
|
updateVisual: function (payload) {
|
var ecModel = this._model;
|
|
// update before setOption
|
if (!ecModel) {
|
return;
|
}
|
|
ecModel.eachSeries(function (seriesModel) {
|
seriesModel.getData().clearAllVisual();
|
});
|
|
doVisualEncoding.call(this, ecModel, payload, true);
|
|
invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);
|
},
|
|
/**
|
* @param {Object} payload
|
* @private
|
*/
|
updateLayout: function (payload) {
|
var ecModel = this._model;
|
|
// update before setOption
|
if (!ecModel) {
|
return;
|
}
|
|
doLayout.call(this, ecModel, payload);
|
|
invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);
|
},
|
|
/**
|
* @param {Object} payload
|
* @private
|
*/
|
prepareAndUpdate: function (payload) {
|
var ecModel = this._model;
|
|
prepareView.call(this, 'component', ecModel);
|
|
prepareView.call(this, 'chart', ecModel);
|
|
// FIXME
|
// ugly
|
if (this.__lastOnlyGraphic) {
|
each(this._componentsViews, function (componentView) {
|
var componentModel = componentView.__model;
|
if (componentModel && componentModel.mainType === 'graphic') {
|
componentView.render(componentModel, ecModel, this._api, payload);
|
updateZ(componentModel, componentView);
|
}
|
}, this);
|
this.__lastOnlyGraphic = false;
|
}
|
else {
|
updateMethods.update.call(this, payload);
|
}
|
}
|
};
|
|
/**
|
* @private
|
*/
|
function updateDirectly(ecIns, method, payload, mainType, subType) {
|
var ecModel = ecIns._model;
|
|
// broadcast
|
if (!mainType) {
|
each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
|
return;
|
}
|
|
var query = {};
|
query[mainType + 'Id'] = payload[mainType + 'Id'];
|
query[mainType + 'Index'] = payload[mainType + 'Index'];
|
query[mainType + 'Name'] = payload[mainType + 'Name'];
|
|
var condition = {mainType: mainType, query: query};
|
subType && (condition.subType = subType); // subType may be '' by parseClassType;
|
|
// If dispatchAction before setOption, do nothing.
|
ecModel && ecModel.eachComponent(condition, function (model, index) {
|
callView(ecIns[
|
mainType === 'series' ? '_chartsMap' : '_componentsMap'
|
][model.__viewId]);
|
}, ecIns);
|
|
function callView(view) {
|
view && view.__alive && view[method] && view[method](
|
view.__model, ecModel, ecIns._api, payload
|
);
|
}
|
}
|
|
/**
|
* Resize the chart
|
* @param {Object} opts
|
* @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
|
* @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
|
* @param {boolean} [opts.silent=false]
|
*/
|
echartsProto.resize = function (opts) {
|
if (true) {
|
zrUtil.assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
|
}
|
|
this[IN_MAIN_PROCESS] = true;
|
|
this._zr.resize(opts);
|
|
var optionChanged = this._model && this._model.resetOption('media');
|
var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';
|
|
updateMethods[updateMethod].call(this);
|
|
// Resize loading effect
|
this._loadingFX && this._loadingFX.resize();
|
|
this[IN_MAIN_PROCESS] = false;
|
|
var silent = opts && opts.silent;
|
|
flushPendingActions.call(this, silent);
|
|
triggerUpdatedEvent.call(this, silent);
|
};
|
|
/**
|
* Show loading effect
|
* @param {string} [name='default']
|
* @param {Object} [cfg]
|
*/
|
echartsProto.showLoading = function (name, cfg) {
|
if (zrUtil.isObject(name)) {
|
cfg = name;
|
name = '';
|
}
|
name = name || 'default';
|
|
this.hideLoading();
|
if (!loadingEffects[name]) {
|
if (true) {
|
console.warn('Loading effects ' + name + ' not exists.');
|
}
|
return;
|
}
|
var el = loadingEffects[name](this._api, cfg);
|
var zr = this._zr;
|
this._loadingFX = el;
|
|
zr.add(el);
|
};
|
|
/**
|
* Hide loading effect
|
*/
|
echartsProto.hideLoading = function () {
|
this._loadingFX && this._zr.remove(this._loadingFX);
|
this._loadingFX = null;
|
};
|
|
/**
|
* @param {Object} eventObj
|
* @return {Object}
|
*/
|
echartsProto.makeActionFromEvent = function (eventObj) {
|
var payload = zrUtil.extend({}, eventObj);
|
payload.type = eventActionMap[eventObj.type];
|
return payload;
|
};
|
|
/**
|
* @pubilc
|
* @param {Object} payload
|
* @param {string} [payload.type] Action type
|
* @param {Object|boolean} [opt] If pass boolean, means opt.silent
|
* @param {boolean} [opt.silent=false] Whether trigger events.
|
* @param {boolean} [opt.flush=undefined]
|
* true: Flush immediately, and then pixel in canvas can be fetched
|
* immediately. Caution: it might affect performance.
|
* false: Not not flush.
|
* undefined: Auto decide whether perform flush.
|
*/
|
echartsProto.dispatchAction = function (payload, opt) {
|
if (!zrUtil.isObject(opt)) {
|
opt = {silent: !!opt};
|
}
|
|
if (!actions[payload.type]) {
|
return;
|
}
|
|
// May dispatchAction in rendering procedure
|
if (this[IN_MAIN_PROCESS]) {
|
this._pendingActions.push(payload);
|
return;
|
}
|
|
doDispatchAction.call(this, payload, opt.silent);
|
|
if (opt.flush) {
|
this._zr.flush(true);
|
}
|
else if (opt.flush !== false && env.browser.weChat) {
|
// In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
|
// hang when sliding page (on touch event), which cause that zr does not
|
// refresh util user interaction finished, which is not expected.
|
// But `dispatchAction` may be called too frequently when pan on touch
|
// screen, which impacts performance if do not throttle them.
|
this._throttledZrFlush();
|
}
|
|
flushPendingActions.call(this, opt.silent);
|
|
triggerUpdatedEvent.call(this, opt.silent);
|
};
|
|
function doDispatchAction(payload, silent) {
|
var payloadType = payload.type;
|
var escapeConnect = payload.escapeConnect;
|
var actionWrap = actions[payloadType];
|
var actionInfo = actionWrap.actionInfo;
|
|
var cptType = (actionInfo.update || 'update').split(':');
|
var updateMethod = cptType.pop();
|
cptType = cptType[0] != null && parseClassType(cptType[0]);
|
|
this[IN_MAIN_PROCESS] = true;
|
|
var payloads = [payload];
|
var batched = false;
|
// Batch action
|
if (payload.batch) {
|
batched = true;
|
payloads = zrUtil.map(payload.batch, function (item) {
|
item = zrUtil.defaults(zrUtil.extend({}, item), payload);
|
item.batch = null;
|
return item;
|
});
|
}
|
|
var eventObjBatch = [];
|
var eventObj;
|
var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
|
|
each(payloads, function (batchItem) {
|
// Action can specify the event by return it.
|
eventObj = actionWrap.action(batchItem, this._model, this._api);
|
// Emit event outside
|
eventObj = eventObj || zrUtil.extend({}, batchItem);
|
// Convert type to eventType
|
eventObj.type = actionInfo.event || eventObj.type;
|
eventObjBatch.push(eventObj);
|
|
// light update does not perform data process, layout and visual.
|
if (isHighDown) {
|
// method, payload, mainType, subType
|
updateDirectly(this, updateMethod, batchItem, 'series');
|
}
|
else if (cptType) {
|
updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
|
}
|
}, this);
|
|
if (updateMethod !== 'none' && !isHighDown && !cptType) {
|
// Still dirty
|
if (this[OPTION_UPDATED]) {
|
// FIXME Pass payload ?
|
updateMethods.prepareAndUpdate.call(this, payload);
|
this[OPTION_UPDATED] = false;
|
}
|
else {
|
updateMethods[updateMethod].call(this, payload);
|
}
|
}
|
|
// Follow the rule of action batch
|
if (batched) {
|
eventObj = {
|
type: actionInfo.event || payloadType,
|
escapeConnect: escapeConnect,
|
batch: eventObjBatch
|
};
|
}
|
else {
|
eventObj = eventObjBatch[0];
|
}
|
|
this[IN_MAIN_PROCESS] = false;
|
|
!silent && this._messageCenter.trigger(eventObj.type, eventObj);
|
}
|
|
function flushPendingActions(silent) {
|
var pendingActions = this._pendingActions;
|
while (pendingActions.length) {
|
var payload = pendingActions.shift();
|
doDispatchAction.call(this, payload, silent);
|
}
|
}
|
|
function triggerUpdatedEvent(silent) {
|
!silent && this.trigger('updated');
|
}
|
|
/**
|
* Register event
|
* @method
|
*/
|
echartsProto.on = createRegisterEventWithLowercaseName('on');
|
echartsProto.off = createRegisterEventWithLowercaseName('off');
|
echartsProto.one = createRegisterEventWithLowercaseName('one');
|
|
/**
|
* @param {string} methodName
|
* @private
|
*/
|
function invokeUpdateMethod(methodName, ecModel, payload) {
|
var api = this._api;
|
|
// Update all components
|
each(this._componentsViews, function (component) {
|
var componentModel = component.__model;
|
component[methodName](componentModel, ecModel, api, payload);
|
|
updateZ(componentModel, component);
|
}, this);
|
|
// Upate all charts
|
ecModel.eachSeries(function (seriesModel, idx) {
|
var chart = this._chartsMap[seriesModel.__viewId];
|
chart[methodName](seriesModel, ecModel, api, payload);
|
|
updateZ(seriesModel, chart);
|
|
updateProgressiveAndBlend(seriesModel, chart);
|
}, this);
|
|
// If use hover layer
|
updateHoverLayerStatus(this._zr, ecModel);
|
|
// Post render
|
each(postUpdateFuncs, function (func) {
|
func(ecModel, api);
|
});
|
}
|
|
/**
|
* Prepare view instances of charts and components
|
* @param {module:echarts/model/Global} ecModel
|
* @private
|
*/
|
function prepareView(type, ecModel) {
|
var isComponent = type === 'component';
|
var viewList = isComponent ? this._componentsViews : this._chartsViews;
|
var viewMap = isComponent ? this._componentsMap : this._chartsMap;
|
var zr = this._zr;
|
|
for (var i = 0; i < viewList.length; i++) {
|
viewList[i].__alive = false;
|
}
|
|
ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {
|
if (isComponent) {
|
if (componentType === 'series') {
|
return;
|
}
|
}
|
else {
|
model = componentType;
|
}
|
|
// Consider: id same and type changed.
|
var viewId = model.id + '_' + model.type;
|
var view = viewMap[viewId];
|
if (!view) {
|
var classType = parseClassType(model.type);
|
var Clazz = isComponent
|
? ComponentView.getClass(classType.main, classType.sub)
|
: ChartView.getClass(classType.sub);
|
if (Clazz) {
|
view = new Clazz();
|
view.init(ecModel, this._api);
|
viewMap[viewId] = view;
|
viewList.push(view);
|
zr.add(view.group);
|
}
|
else {
|
// Error
|
return;
|
}
|
}
|
|
model.__viewId = view.__id = viewId;
|
view.__alive = true;
|
view.__model = model;
|
view.group.__ecComponentInfo = {
|
mainType: model.mainType,
|
index: model.componentIndex
|
};
|
}, this);
|
|
for (var i = 0; i < viewList.length;) {
|
var view = viewList[i];
|
if (!view.__alive) {
|
zr.remove(view.group);
|
view.dispose(ecModel, this._api);
|
viewList.splice(i, 1);
|
delete viewMap[view.__id];
|
view.__id = view.group.__ecComponentInfo = null;
|
}
|
else {
|
i++;
|
}
|
}
|
}
|
|
/**
|
* Processor data in each series
|
*
|
* @param {module:echarts/model/Global} ecModel
|
* @private
|
*/
|
function processData(ecModel, api) {
|
each(dataProcessorFuncs, function (process) {
|
process.func(ecModel, api);
|
});
|
}
|
|
/**
|
* @private
|
*/
|
function stackSeriesData(ecModel) {
|
var stackedDataMap = {};
|
ecModel.eachSeries(function (series) {
|
var stack = series.get('stack');
|
var data = series.getData();
|
if (stack && data.type === 'list') {
|
var previousStack = stackedDataMap[stack];
|
if (previousStack) {
|
data.stackedOn = previousStack;
|
}
|
stackedDataMap[stack] = data;
|
}
|
});
|
}
|
|
/**
|
* Layout before each chart render there series, special visual encoding stage
|
*
|
* @param {module:echarts/model/Global} ecModel
|
* @private
|
*/
|
function doLayout(ecModel, payload) {
|
var api = this._api;
|
each(visualFuncs, function (visual) {
|
if (visual.isLayout) {
|
visual.func(ecModel, api, payload);
|
}
|
});
|
}
|
|
/**
|
* Encode visual infomation from data after data processing
|
*
|
* @param {module:echarts/model/Global} ecModel
|
* @param {object} layout
|
* @param {boolean} [excludesLayout]
|
* @private
|
*/
|
function doVisualEncoding(ecModel, payload, excludesLayout) {
|
var api = this._api;
|
ecModel.clearColorPalette();
|
ecModel.eachSeries(function (seriesModel) {
|
seriesModel.clearColorPalette();
|
});
|
each(visualFuncs, function (visual) {
|
(!excludesLayout || !visual.isLayout)
|
&& visual.func(ecModel, api, payload);
|
});
|
}
|
|
/**
|
* Render each chart and component
|
* @private
|
*/
|
function doRender(ecModel, payload) {
|
var api = this._api;
|
// Render all components
|
each(this._componentsViews, function (componentView) {
|
var componentModel = componentView.__model;
|
componentView.render(componentModel, ecModel, api, payload);
|
|
updateZ(componentModel, componentView);
|
}, this);
|
|
each(this._chartsViews, function (chart) {
|
chart.__alive = false;
|
}, this);
|
|
// Render all charts
|
ecModel.eachSeries(function (seriesModel, idx) {
|
var chartView = this._chartsMap[seriesModel.__viewId];
|
chartView.__alive = true;
|
chartView.render(seriesModel, ecModel, api, payload);
|
|
chartView.group.silent = !!seriesModel.get('silent');
|
|
updateZ(seriesModel, chartView);
|
|
updateProgressiveAndBlend(seriesModel, chartView);
|
|
}, this);
|
|
// If use hover layer
|
updateHoverLayerStatus(this._zr, ecModel);
|
|
// Remove groups of unrendered charts
|
each(this._chartsViews, function (chart) {
|
if (!chart.__alive) {
|
chart.remove(ecModel, api);
|
}
|
}, this);
|
}
|
|
var MOUSE_EVENT_NAMES = [
|
'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
|
'mousedown', 'mouseup', 'globalout', 'contextmenu'
|
];
|
/**
|
* @private
|
*/
|
echartsProto._initEvents = function () {
|
each(MOUSE_EVENT_NAMES, function (eveName) {
|
this._zr.on(eveName, function (e) {
|
var ecModel = this.getModel();
|
var el = e.target;
|
var params;
|
|
// no e.target when 'globalout'.
|
if (eveName === 'globalout') {
|
params = {};
|
}
|
else if (el && el.dataIndex != null) {
|
var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
|
params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
|
}
|
// If element has custom eventData of components
|
else if (el && el.eventData) {
|
params = zrUtil.extend({}, el.eventData);
|
}
|
|
if (params) {
|
params.event = e;
|
params.type = eveName;
|
this.trigger(eveName, params);
|
}
|
|
}, this);
|
}, this);
|
|
each(eventActionMap, function (actionType, eventType) {
|
this._messageCenter.on(eventType, function (event) {
|
this.trigger(eventType, event);
|
}, this);
|
}, this);
|
};
|
|
/**
|
* @return {boolean}
|
*/
|
echartsProto.isDisposed = function () {
|
return this._disposed;
|
};
|
|
/**
|
* Clear
|
*/
|
echartsProto.clear = function () {
|
this.setOption({ series: [] }, true);
|
};
|
|
/**
|
* Dispose instance
|
*/
|
echartsProto.dispose = function () {
|
if (this._disposed) {
|
if (true) {
|
console.warn('Instance ' + this.id + ' has been disposed');
|
}
|
return;
|
}
|
this._disposed = true;
|
|
var api = this._api;
|
var ecModel = this._model;
|
|
each(this._componentsViews, function (component) {
|
component.dispose(ecModel, api);
|
});
|
each(this._chartsViews, function (chart) {
|
chart.dispose(ecModel, api);
|
});
|
|
// Dispose after all views disposed
|
this._zr.dispose();
|
|
delete instances[this.id];
|
};
|
|
zrUtil.mixin(ECharts, Eventful);
|
|
function updateHoverLayerStatus(zr, ecModel) {
|
var storage = zr.storage;
|
var elCount = 0;
|
storage.traverse(function (el) {
|
if (!el.isGroup) {
|
elCount++;
|
}
|
});
|
if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
|
storage.traverse(function (el) {
|
if (!el.isGroup) {
|
el.useHoverLayer = true;
|
}
|
});
|
}
|
}
|
|
/**
|
* Update chart progressive and blend.
|
* @param {module:echarts/model/Series|module:echarts/model/Component} model
|
* @param {module:echarts/view/Component|module:echarts/view/Chart} view
|
*/
|
function updateProgressiveAndBlend(seriesModel, chartView) {
|
// Progressive configuration
|
var elCount = 0;
|
chartView.group.traverse(function (el) {
|
if (el.type !== 'group' && !el.ignore) {
|
elCount++;
|
}
|
});
|
var frameDrawNum = +seriesModel.get('progressive');
|
var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node;
|
if (needProgressive) {
|
chartView.group.traverse(function (el) {
|
// FIXME marker and other components
|
if (!el.isGroup) {
|
el.progressive = needProgressive ?
|
Math.floor(elCount++ / frameDrawNum) : -1;
|
if (needProgressive) {
|
el.stopAnimation(true);
|
}
|
}
|
});
|
}
|
|
// Blend configration
|
var blendMode = seriesModel.get('blendMode') || null;
|
if (true) {
|
if (!env.canvasSupported && blendMode && blendMode !== 'source-over') {
|
console.warn('Only canvas support blendMode');
|
}
|
}
|
chartView.group.traverse(function (el) {
|
// FIXME marker and other components
|
if (!el.isGroup) {
|
el.setStyle('blend', blendMode);
|
}
|
});
|
}
|
|
/**
|
* @param {module:echarts/model/Series|module:echarts/model/Component} model
|
* @param {module:echarts/view/Component|module:echarts/view/Chart} view
|
*/
|
function updateZ(model, view) {
|
var z = model.get('z');
|
var zlevel = model.get('zlevel');
|
// Set z and zlevel
|
view.group.traverse(function (el) {
|
if (el.type !== 'group') {
|
z != null && (el.z = z);
|
zlevel != null && (el.zlevel = zlevel);
|
}
|
});
|
}
|
|
function createExtensionAPI(ecInstance) {
|
var coordSysMgr = ecInstance._coordSysMgr;
|
return zrUtil.extend(new ExtensionAPI(ecInstance), {
|
// Inject methods
|
getCoordinateSystems: zrUtil.bind(
|
coordSysMgr.getCoordinateSystems, coordSysMgr
|
),
|
getComponentByElement: function (el) {
|
while (el) {
|
var modelInfo = el.__ecComponentInfo;
|
if (modelInfo != null) {
|
return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
|
}
|
el = el.parent;
|
}
|
}
|
});
|
}
|
|
/**
|
* @type {Object} key: actionType.
|
* @inner
|
*/
|
var actions = {};
|
|
/**
|
* Map eventType to actionType
|
* @type {Object}
|
*/
|
var eventActionMap = {};
|
|
/**
|
* Data processor functions of each stage
|
* @type {Array.<Object.<string, Function>>}
|
* @inner
|
*/
|
var dataProcessorFuncs = [];
|
|
/**
|
* @type {Array.<Function>}
|
* @inner
|
*/
|
var optionPreprocessorFuncs = [];
|
|
/**
|
* @type {Array.<Function>}
|
* @inner
|
*/
|
var postUpdateFuncs = [];
|
|
/**
|
* Visual encoding functions of each stage
|
* @type {Array.<Object.<string, Function>>}
|
* @inner
|
*/
|
var visualFuncs = [];
|
/**
|
* Theme storage
|
* @type {Object.<key, Object>}
|
*/
|
var themeStorage = {};
|
/**
|
* Loading effects
|
*/
|
var loadingEffects = {};
|
|
|
var instances = {};
|
var connectedGroups = {};
|
|
var idBase = new Date() - 0;
|
var groupIdBase = new Date() - 0;
|
var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
|
/**
|
* @alias module:echarts
|
*/
|
var echarts = {
|
/**
|
* @type {number}
|
*/
|
version: '3.5.4',
|
dependencies: {
|
zrender: '3.4.4'
|
}
|
};
|
|
function enableConnect(chart) {
|
var STATUS_PENDING = 0;
|
var STATUS_UPDATING = 1;
|
var STATUS_UPDATED = 2;
|
var STATUS_KEY = '__connectUpdateStatus';
|
|
function updateConnectedChartsStatus(charts, status) {
|
for (var i = 0; i < charts.length; i++) {
|
var otherChart = charts[i];
|
otherChart[STATUS_KEY] = status;
|
}
|
}
|
|
zrUtil.each(eventActionMap, function (actionType, eventType) {
|
chart._messageCenter.on(eventType, function (event) {
|
if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
|
if (event && event.escapeConnect) {
|
return;
|
}
|
|
var action = chart.makeActionFromEvent(event);
|
var otherCharts = [];
|
|
zrUtil.each(instances, function (otherChart) {
|
if (otherChart !== chart && otherChart.group === chart.group) {
|
otherCharts.push(otherChart);
|
}
|
});
|
|
updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
|
each(otherCharts, function (otherChart) {
|
if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
|
otherChart.dispatchAction(action);
|
}
|
});
|
updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
|
}
|
});
|
});
|
}
|
|
/**
|
* @param {HTMLDomElement} dom
|
* @param {Object} [theme]
|
* @param {Object} opts
|
* @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
|
* @param {string} [opts.renderer] Currently only 'canvas' is supported.
|
* @param {number} [opts.width] Use clientWidth of the input `dom` by default.
|
* Can be 'auto' (the same as null/undefined)
|
* @param {number} [opts.height] Use clientHeight of the input `dom` by default.
|
* Can be 'auto' (the same as null/undefined)
|
*/
|
echarts.init = function (dom, theme, opts) {
|
if (true) {
|
// Check version
|
if ((zrender.version.replace('.', '') - 0) < (echarts.dependencies.zrender.replace('.', '') - 0)) {
|
throw new Error(
|
'ZRender ' + zrender.version
|
+ ' is too old for ECharts ' + echarts.version
|
+ '. Current version need ZRender '
|
+ echarts.dependencies.zrender + '+'
|
);
|
}
|
if (!dom) {
|
throw new Error('Initialize failed: invalid dom.');
|
}
|
if (zrUtil.isDom(dom)
|
&& dom.nodeName.toUpperCase() !== 'CANVAS'
|
&& (
|
(!dom.clientWidth && (!opts || opts.width == null))
|
|| (!dom.clientHeight && (!opts || opts.height == null))
|
)
|
) {
|
console.warn('Can\'t get dom width or height');
|
}
|
}
|
|
var chart = new ECharts(dom, theme, opts);
|
chart.id = 'ec_' + idBase++;
|
instances[chart.id] = chart;
|
|
dom.setAttribute &&
|
dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);
|
|
enableConnect(chart);
|
|
return chart;
|
};
|
|
/**
|
* @return {string|Array.<module:echarts~ECharts>} groupId
|
*/
|
echarts.connect = function (groupId) {
|
// Is array of charts
|
if (zrUtil.isArray(groupId)) {
|
var charts = groupId;
|
groupId = null;
|
// If any chart has group
|
zrUtil.each(charts, function (chart) {
|
if (chart.group != null) {
|
groupId = chart.group;
|
}
|
});
|
groupId = groupId || ('g_' + groupIdBase++);
|
zrUtil.each(charts, function (chart) {
|
chart.group = groupId;
|
});
|
}
|
connectedGroups[groupId] = true;
|
return groupId;
|
};
|
|
/**
|
* @DEPRECATED
|
* @return {string} groupId
|
*/
|
echarts.disConnect = function (groupId) {
|
connectedGroups[groupId] = false;
|
};
|
|
/**
|
* @return {string} groupId
|
*/
|
echarts.disconnect = echarts.disConnect;
|
|
/**
|
* Dispose a chart instance
|
* @param {module:echarts~ECharts|HTMLDomElement|string} chart
|
*/
|
echarts.dispose = function (chart) {
|
if (zrUtil.isDom(chart)) {
|
chart = echarts.getInstanceByDom(chart);
|
}
|
else if (typeof chart === 'string') {
|
chart = instances[chart];
|
}
|
if ((chart instanceof ECharts) && !chart.isDisposed()) {
|
chart.dispose();
|
}
|
};
|
|
/**
|
* @param {HTMLDomElement} dom
|
* @return {echarts~ECharts}
|
*/
|
echarts.getInstanceByDom = function (dom) {
|
var key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
|
return instances[key];
|
};
|
/**
|
* @param {string} key
|
* @return {echarts~ECharts}
|
*/
|
echarts.getInstanceById = function (key) {
|
return instances[key];
|
};
|
|
/**
|
* Register theme
|
*/
|
echarts.registerTheme = function (name, theme) {
|
themeStorage[name] = theme;
|
};
|
|
/**
|
* Register option preprocessor
|
* @param {Function} preprocessorFunc
|
*/
|
echarts.registerPreprocessor = function (preprocessorFunc) {
|
optionPreprocessorFuncs.push(preprocessorFunc);
|
};
|
|
/**
|
* @param {number} [priority=1000]
|
* @param {Function} processorFunc
|
*/
|
echarts.registerProcessor = function (priority, processorFunc) {
|
if (typeof priority === 'function') {
|
processorFunc = priority;
|
priority = PRIORITY_PROCESSOR_FILTER;
|
}
|
if (true) {
|
if (isNaN(priority)) {
|
throw new Error('Unkown processor priority');
|
}
|
}
|
dataProcessorFuncs.push({
|
prio: priority,
|
func: processorFunc
|
});
|
};
|
|
/**
|
* Register postUpdater
|
* @param {Function} postUpdateFunc
|
*/
|
echarts.registerPostUpdate = function (postUpdateFunc) {
|
postUpdateFuncs.push(postUpdateFunc);
|
};
|
|
/**
|
* Usage:
|
* registerAction('someAction', 'someEvent', function () { ... });
|
* registerAction('someAction', function () { ... });
|
* registerAction(
|
* {type: 'someAction', event: 'someEvent', update: 'updateView'},
|
* function () { ... }
|
* );
|
*
|
* @param {(string|Object)} actionInfo
|
* @param {string} actionInfo.type
|
* @param {string} [actionInfo.event]
|
* @param {string} [actionInfo.update]
|
* @param {string} [eventName]
|
* @param {Function} action
|
*/
|
echarts.registerAction = function (actionInfo, eventName, action) {
|
if (typeof eventName === 'function') {
|
action = eventName;
|
eventName = '';
|
}
|
var actionType = zrUtil.isObject(actionInfo)
|
? actionInfo.type
|
: ([actionInfo, actionInfo = {
|
event: eventName
|
}][0]);
|
|
// Event name is all lowercase
|
actionInfo.event = (actionInfo.event || actionType).toLowerCase();
|
eventName = actionInfo.event;
|
|
// Validate action type and event name.
|
zrUtil.assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
|
|
if (!actions[actionType]) {
|
actions[actionType] = {action: action, actionInfo: actionInfo};
|
}
|
eventActionMap[eventName] = actionType;
|
};
|
|
/**
|
* @param {string} type
|
* @param {*} CoordinateSystem
|
*/
|
echarts.registerCoordinateSystem = function (type, CoordinateSystem) {
|
CoordinateSystemManager.register(type, CoordinateSystem);
|
};
|
|
/**
|
* Layout is a special stage of visual encoding
|
* Most visual encoding like color are common for different chart
|
* But each chart has it's own layout algorithm
|
*
|
* @param {number} [priority=1000]
|
* @param {Function} layoutFunc
|
*/
|
echarts.registerLayout = function (priority, layoutFunc) {
|
if (typeof priority === 'function') {
|
layoutFunc = priority;
|
priority = PRIORITY_VISUAL_LAYOUT;
|
}
|
if (true) {
|
if (isNaN(priority)) {
|
throw new Error('Unkown layout priority');
|
}
|
}
|
visualFuncs.push({
|
prio: priority,
|
func: layoutFunc,
|
isLayout: true
|
});
|
};
|
|
/**
|
* @param {number} [priority=3000]
|
* @param {Function} visualFunc
|
*/
|
echarts.registerVisual = function (priority, visualFunc) {
|
if (typeof priority === 'function') {
|
visualFunc = priority;
|
priority = PRIORITY_VISUAL_CHART;
|
}
|
if (true) {
|
if (isNaN(priority)) {
|
throw new Error('Unkown visual priority');
|
}
|
}
|
visualFuncs.push({
|
prio: priority,
|
func: visualFunc
|
});
|
};
|
|
/**
|
* @param {string} name
|
*/
|
echarts.registerLoading = function (name, loadingFx) {
|
loadingEffects[name] = loadingFx;
|
};
|
|
/**
|
* @param {Object} opts
|
* @param {string} [superClass]
|
*/
|
echarts.extendComponentModel = function (opts/*, superClass*/) {
|
// var Clazz = ComponentModel;
|
// if (superClass) {
|
// var classType = parseClassType(superClass);
|
// Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
|
// }
|
return ComponentModel.extend(opts);
|
};
|
|
/**
|
* @param {Object} opts
|
* @param {string} [superClass]
|
*/
|
echarts.extendComponentView = function (opts/*, superClass*/) {
|
// var Clazz = ComponentView;
|
// if (superClass) {
|
// var classType = parseClassType(superClass);
|
// Clazz = ComponentView.getClass(classType.main, classType.sub, true);
|
// }
|
return ComponentView.extend(opts);
|
};
|
|
/**
|
* @param {Object} opts
|
* @param {string} [superClass]
|
*/
|
echarts.extendSeriesModel = function (opts/*, superClass*/) {
|
// var Clazz = SeriesModel;
|
// if (superClass) {
|
// superClass = 'series.' + superClass.replace('series.', '');
|
// var classType = parseClassType(superClass);
|
// Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
|
// }
|
return SeriesModel.extend(opts);
|
};
|
|
/**
|
* @param {Object} opts
|
* @param {string} [superClass]
|
*/
|
echarts.extendChartView = function (opts/*, superClass*/) {
|
// var Clazz = ChartView;
|
// if (superClass) {
|
// superClass = superClass.replace('series.', '');
|
// var classType = parseClassType(superClass);
|
// Clazz = ChartView.getClass(classType.main, true);
|
// }
|
return ChartView.extend(opts);
|
};
|
|
/**
|
* ZRender need a canvas context to do measureText.
|
* But in node environment canvas may be created by node-canvas.
|
* So we need to specify how to create a canvas instead of using document.createElement('canvas')
|
*
|
* Be careful of using it in the browser.
|
*
|
* @param {Function} creator
|
* @example
|
* var Canvas = require('canvas');
|
* var echarts = require('echarts');
|
* echarts.setCanvasCreator(function () {
|
* // Small size is enough.
|
* return new Canvas(32, 32);
|
* });
|
*/
|
echarts.setCanvasCreator = function (creator) {
|
zrUtil.createCanvas = creator;
|
};
|
|
echarts.registerVisual(PRIORITY_VISUAL_GLOBAL, __webpack_require__(94));
|
echarts.registerPreprocessor(__webpack_require__(95));
|
echarts.registerLoading('default', __webpack_require__(97));
|
|
// Default action
|
echarts.registerAction({
|
type: 'highlight',
|
event: 'highlight',
|
update: 'highlight'
|
}, zrUtil.noop);
|
echarts.registerAction({
|
type: 'downplay',
|
event: 'downplay',
|
update: 'downplay'
|
}, zrUtil.noop);
|
|
|
// --------
|
// Exports
|
// --------
|
echarts.zrender = zrender;
|
|
echarts.List = __webpack_require__(98);
|
echarts.Model = __webpack_require__(12);
|
|
echarts.Axis = __webpack_require__(100);
|
|
echarts.graphic = __webpack_require__(44);
|
echarts.number = __webpack_require__(7);
|
echarts.format = __webpack_require__(6);
|
echarts.throttle = throttle.throttle;
|
echarts.matrix = __webpack_require__(11);
|
echarts.vector = __webpack_require__(10);
|
echarts.color = __webpack_require__(39);
|
|
echarts.util = {};
|
each([
|
'map', 'each', 'filter', 'indexOf', 'inherits', 'reduce', 'filter',
|
'bind', 'curry', 'isArray', 'isString', 'isObject', 'isFunction',
|
'extend', 'defaults', 'clone', 'merge'
|
],
|
function (name) {
|
echarts.util[name] = zrUtil[name];
|
}
|
);
|
|
echarts.helper = __webpack_require__(101);
|
|
|
// PRIORITY
|
echarts.PRIORITY = {
|
PROCESSOR: {
|
FILTER: PRIORITY_PROCESSOR_FILTER,
|
STATISTIC: PRIORITY_PROCESSOR_STATISTIC
|
},
|
VISUAL: {
|
LAYOUT: PRIORITY_VISUAL_LAYOUT,
|
GLOBAL: PRIORITY_VISUAL_GLOBAL,
|
CHART: PRIORITY_VISUAL_CHART,
|
COMPONENT: PRIORITY_VISUAL_COMPONENT,
|
BRUSH: PRIORITY_VISUAL_BRUSH
|
}
|
};
|
|
module.exports = echarts;
|
|
|
/***/ },
|
/* 2 */
|
/***/ function(module, exports) {
|
|
/**
|
* echarts设备环境识别
|
*
|
* @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
|
* @author firede[firede@firede.us]
|
* @desc thanks zepto.
|
*/
|
|
var env = {};
|
if (typeof navigator === 'undefined') {
|
// In node
|
env = {
|
browser: {},
|
os: {},
|
node: true,
|
// Assume canvas is supported
|
canvasSupported: true
|
};
|
}
|
else {
|
env = detect(navigator.userAgent);
|
}
|
|
module.exports = env;
|
|
// Zepto.js
|
// (c) 2010-2013 Thomas Fuchs
|
// Zepto.js may be freely distributed under the MIT license.
|
|
function detect(ua) {
|
var os = {};
|
var browser = {};
|
// var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
|
// var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
|
// var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
|
// var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
|
// var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
|
// var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
|
// var touchpad = webos && ua.match(/TouchPad/);
|
// var kindle = ua.match(/Kindle\/([\d.]+)/);
|
// var silk = ua.match(/Silk\/([\d._]+)/);
|
// var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
|
// var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
|
// var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
|
// var playbook = ua.match(/PlayBook/);
|
// var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
|
var firefox = ua.match(/Firefox\/([\d.]+)/);
|
// var safari = webkit && ua.match(/Mobile\//) && !chrome;
|
// var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
|
var ie = ua.match(/MSIE\s([\d.]+)/)
|
// IE 11 Trident/7.0; rv:11.0
|
|| ua.match(/Trident\/.+?rv:(([\d.]+))/);
|
var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
|
|
var weChat = (/micromessenger/i).test(ua);
|
|
// Todo: clean this up with a better OS/browser seperation:
|
// - discern (more) between multiple browsers on android
|
// - decide if kindle fire in silk mode is android or not
|
// - Firefox on Android doesn't specify the Android version
|
// - possibly devide in os, device and browser hashes
|
|
// if (browser.webkit = !!webkit) browser.version = webkit[1];
|
|
// if (android) os.android = true, os.version = android[2];
|
// if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
|
// if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
|
// if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
|
// if (webos) os.webos = true, os.version = webos[2];
|
// if (touchpad) os.touchpad = true;
|
// if (blackberry) os.blackberry = true, os.version = blackberry[2];
|
// if (bb10) os.bb10 = true, os.version = bb10[2];
|
// if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
|
// if (playbook) browser.playbook = true;
|
// if (kindle) os.kindle = true, os.version = kindle[1];
|
// if (silk) browser.silk = true, browser.version = silk[1];
|
// if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
|
// if (chrome) browser.chrome = true, browser.version = chrome[1];
|
if (firefox) {
|
browser.firefox = true;
|
browser.version = firefox[1];
|
}
|
// if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
|
// if (webview) browser.webview = true;
|
|
if (ie) {
|
browser.ie = true;
|
browser.version = ie[1];
|
}
|
|
if (edge) {
|
browser.edge = true;
|
browser.version = edge[1];
|
}
|
|
// It is difficult to detect WeChat in Win Phone precisely, because ua can
|
// not be set on win phone. So we do not consider Win Phone.
|
if (weChat) {
|
browser.weChat = true;
|
}
|
|
// os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
|
// (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
|
// os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
|
// (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
|
// (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
|
|
return {
|
browser: browser,
|
os: os,
|
node: false,
|
// 原生canvas支持,改极端点了
|
// canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
|
canvasSupported : document.createElement('canvas').getContext ? true : false,
|
// @see <http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript>
|
// works on most browsers
|
// IE10/11 does not support touch event, and MS Edge supports them but not by
|
// default, so we dont check navigator.maxTouchPoints for them here.
|
touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
|
// <http://caniuse.com/#search=pointer%20event>.
|
pointerEventsSupported: 'onpointerdown' in window
|
// Firefox supports pointer but not by default, only MS browsers are reliable on pointer
|
// events currently. So we dont use that on other browsers unless tested sufficiently.
|
// Although IE 10 supports pointer event, it use old style and is different from the
|
// standard. So we exclude that. (IE 10 is hardly used on touch device)
|
&& (browser.edge || (browser.ie && browser.version >= 11))
|
};
|
}
|
|
|
/***/ },
|
/* 3 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* ECharts global model
|
*
|
* @module {echarts/model/Global}
|
*/
|
|
|
|
/**
|
* Caution: If the mechanism should be changed some day, these cases
|
* should be considered:
|
*
|
* (1) In `merge option` mode, if using the same option to call `setOption`
|
* many times, the result should be the same (try our best to ensure that).
|
* (2) In `merge option` mode, if a component has no id/name specified, it
|
* will be merged by index, and the result sequence of the components is
|
* consistent to the original sequence.
|
* (3) `reset` feature (in toolbox). Find detailed info in comments about
|
* `mergeOption` in module:echarts/model/OptionManager.
|
*/
|
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var Model = __webpack_require__(12);
|
var each = zrUtil.each;
|
var filter = zrUtil.filter;
|
var map = zrUtil.map;
|
var isArray = zrUtil.isArray;
|
var indexOf = zrUtil.indexOf;
|
var isObject = zrUtil.isObject;
|
|
var ComponentModel = __webpack_require__(19);
|
|
var globalDefault = __webpack_require__(23);
|
|
var OPTION_INNER_KEY = '\0_ec_inner';
|
|
/**
|
* @alias module:echarts/model/Global
|
*
|
* @param {Object} option
|
* @param {module:echarts/model/Model} parentModel
|
* @param {Object} theme
|
*/
|
var GlobalModel = Model.extend({
|
|
constructor: GlobalModel,
|
|
init: function (option, parentModel, theme, optionManager) {
|
theme = theme || {};
|
|
this.option = null; // Mark as not initialized.
|
|
/**
|
* @type {module:echarts/model/Model}
|
* @private
|
*/
|
this._theme = new Model(theme);
|
|
/**
|
* @type {module:echarts/model/OptionManager}
|
*/
|
this._optionManager = optionManager;
|
},
|
|
setOption: function (option, optionPreprocessorFuncs, onlyGraphic) {
|
zrUtil.assert(
|
!(OPTION_INNER_KEY in option),
|
'please use chart.getOption()'
|
);
|
|
this._optionManager.setOption(option, optionPreprocessorFuncs);
|
|
this.resetOption(null, onlyGraphic);
|
},
|
|
/**
|
* @param {string} type null/undefined: reset all.
|
* 'recreate': force recreate all.
|
* 'timeline': only reset timeline option
|
* 'media': only reset media query option
|
* @return {boolean} Whether option changed.
|
*/
|
resetOption: function (type, onlyGraphic) {
|
var optionChanged = false;
|
var optionManager = this._optionManager;
|
|
if (!type || type === 'recreate') {
|
var baseOption = optionManager.mountOption(type === 'recreate');
|
|
if (!this.option || type === 'recreate') {
|
initBase.call(this, baseOption);
|
}
|
else {
|
// If only graphic, other series and component will not
|
// go through update process, data should not be restored.
|
// Otherwise grphic els mounted on data will be eliminated
|
// and downplay will not work.
|
!onlyGraphic && this.restoreData();
|
this.mergeOption(baseOption);
|
}
|
optionChanged = true;
|
}
|
|
if (type === 'timeline' || type === 'media') {
|
this.restoreData();
|
}
|
|
if (!type || type === 'recreate' || type === 'timeline') {
|
var timelineOption = optionManager.getTimelineOption(this);
|
timelineOption && (this.mergeOption(timelineOption), optionChanged = true);
|
}
|
|
if (!type || type === 'recreate' || type === 'media') {
|
var mediaOptions = optionManager.getMediaOption(this, this._api);
|
if (mediaOptions.length) {
|
each(mediaOptions, function (mediaOption) {
|
this.mergeOption(mediaOption, optionChanged = true);
|
}, this);
|
}
|
}
|
|
return optionChanged;
|
},
|
|
/**
|
* @protected
|
*/
|
mergeOption: function (newOption) {
|
var option = this.option;
|
var componentsMap = this._componentsMap;
|
var newCptTypes = [];
|
|
// 如果不存在对应的 component model 则直接 merge
|
each(newOption, function (componentOption, mainType) {
|
if (componentOption == null) {
|
return;
|
}
|
|
if (!ComponentModel.hasClass(mainType)) {
|
option[mainType] = option[mainType] == null
|
? zrUtil.clone(componentOption)
|
: zrUtil.merge(option[mainType], componentOption, true);
|
}
|
else {
|
newCptTypes.push(mainType);
|
}
|
});
|
|
// FIXME OPTION 同步是否要改回原来的
|
ComponentModel.topologicalTravel(
|
newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this
|
);
|
|
this._seriesIndices = this._seriesIndices || [];
|
|
function visitComponent(mainType, dependencies) {
|
var newCptOptionList = modelUtil.normalizeToArray(newOption[mainType]);
|
|
var mapResult = modelUtil.mappingToExists(
|
componentsMap[mainType], newCptOptionList
|
);
|
|
modelUtil.makeIdAndName(mapResult);
|
|
// Set mainType and complete subType.
|
each(mapResult, function (item, index) {
|
var opt = item.option;
|
if (isObject(opt)) {
|
item.keyInfo.mainType = mainType;
|
item.keyInfo.subType = determineSubType(mainType, opt, item.exist);
|
}
|
});
|
|
var dependentModels = getComponentsByTypes(
|
componentsMap, dependencies
|
);
|
|
option[mainType] = [];
|
componentsMap[mainType] = [];
|
|
each(mapResult, function (resultItem, index) {
|
var componentModel = resultItem.exist;
|
var newCptOption = resultItem.option;
|
|
zrUtil.assert(
|
isObject(newCptOption) || componentModel,
|
'Empty component definition'
|
);
|
|
// Consider where is no new option and should be merged using {},
|
// see removeEdgeAndAdd in topologicalTravel and
|
// ComponentModel.getAllClassMainTypes.
|
if (!newCptOption) {
|
componentModel.mergeOption({}, this);
|
componentModel.optionUpdated({}, false);
|
}
|
else {
|
var ComponentModelClass = ComponentModel.getClass(
|
mainType, resultItem.keyInfo.subType, true
|
);
|
|
if (componentModel && componentModel instanceof ComponentModelClass) {
|
componentModel.name = resultItem.keyInfo.name;
|
componentModel.mergeOption(newCptOption, this);
|
componentModel.optionUpdated(newCptOption, false);
|
}
|
else {
|
// PENDING Global as parent ?
|
var extraOpt = zrUtil.extend(
|
{
|
dependentModels: dependentModels,
|
componentIndex: index
|
},
|
resultItem.keyInfo
|
);
|
componentModel = new ComponentModelClass(
|
newCptOption, this, this, extraOpt
|
);
|
zrUtil.extend(componentModel, extraOpt);
|
componentModel.init(newCptOption, this, this, extraOpt);
|
// Call optionUpdated after init.
|
// newCptOption has been used as componentModel.option
|
// and may be merged with theme and default, so pass null
|
// to avoid confusion.
|
componentModel.optionUpdated(null, true);
|
}
|
}
|
|
componentsMap[mainType][index] = componentModel;
|
option[mainType][index] = componentModel.option;
|
}, this);
|
|
// Backup series for filtering.
|
if (mainType === 'series') {
|
this._seriesIndices = createSeriesIndices(componentsMap.series);
|
}
|
}
|
},
|
|
/**
|
* Get option for output (cloned option and inner info removed)
|
* @public
|
* @return {Object}
|
*/
|
getOption: function () {
|
var option = zrUtil.clone(this.option);
|
|
each(option, function (opts, mainType) {
|
if (ComponentModel.hasClass(mainType)) {
|
var opts = modelUtil.normalizeToArray(opts);
|
for (var i = opts.length - 1; i >= 0; i--) {
|
// Remove options with inner id.
|
if (modelUtil.isIdInner(opts[i])) {
|
opts.splice(i, 1);
|
}
|
}
|
option[mainType] = opts;
|
}
|
});
|
|
delete option[OPTION_INNER_KEY];
|
|
return option;
|
},
|
|
/**
|
* @return {module:echarts/model/Model}
|
*/
|
getTheme: function () {
|
return this._theme;
|
},
|
|
/**
|
* @param {string} mainType
|
* @param {number} [idx=0]
|
* @return {module:echarts/model/Component}
|
*/
|
getComponent: function (mainType, idx) {
|
var list = this._componentsMap[mainType];
|
if (list) {
|
return list[idx || 0];
|
}
|
},
|
|
/**
|
* If none of index and id and name used, return all components with mainType.
|
* @param {Object} condition
|
* @param {string} condition.mainType
|
* @param {string} [condition.subType] If ignore, only query by mainType
|
* @param {number|Array.<number>} [condition.index] Either input index or id or name.
|
* @param {string|Array.<string>} [condition.id] Either input index or id or name.
|
* @param {string|Array.<string>} [condition.name] Either input index or id or name.
|
* @return {Array.<module:echarts/model/Component>}
|
*/
|
queryComponents: function (condition) {
|
var mainType = condition.mainType;
|
if (!mainType) {
|
return [];
|
}
|
|
var index = condition.index;
|
var id = condition.id;
|
var name = condition.name;
|
|
var cpts = this._componentsMap[mainType];
|
|
if (!cpts || !cpts.length) {
|
return [];
|
}
|
|
var result;
|
|
if (index != null) {
|
if (!isArray(index)) {
|
index = [index];
|
}
|
result = filter(map(index, function (idx) {
|
return cpts[idx];
|
}), function (val) {
|
return !!val;
|
});
|
}
|
else if (id != null) {
|
var isIdArray = isArray(id);
|
result = filter(cpts, function (cpt) {
|
return (isIdArray && indexOf(id, cpt.id) >= 0)
|
|| (!isIdArray && cpt.id === id);
|
});
|
}
|
else if (name != null) {
|
var isNameArray = isArray(name);
|
result = filter(cpts, function (cpt) {
|
return (isNameArray && indexOf(name, cpt.name) >= 0)
|
|| (!isNameArray && cpt.name === name);
|
});
|
}
|
else {
|
// Return all components with mainType
|
result = cpts.slice();
|
}
|
|
return filterBySubType(result, condition);
|
},
|
|
/**
|
* The interface is different from queryComponents,
|
* which is convenient for inner usage.
|
*
|
* @usage
|
* var result = findComponents(
|
* {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}
|
* );
|
* var result = findComponents(
|
* {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}
|
* );
|
* var result = findComponents(
|
* {mainType: 'series'},
|
* function (model, index) {...}
|
* );
|
* // result like [component0, componnet1, ...]
|
*
|
* @param {Object} condition
|
* @param {string} condition.mainType Mandatory.
|
* @param {string} [condition.subType] Optional.
|
* @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},
|
* where xxx is mainType.
|
* If query attribute is null/undefined or has no index/id/name,
|
* do not filtering by query conditions, which is convenient for
|
* no-payload situations or when target of action is global.
|
* @param {Function} [condition.filter] parameter: component, return boolean.
|
* @return {Array.<module:echarts/model/Component>}
|
*/
|
findComponents: function (condition) {
|
var query = condition.query;
|
var mainType = condition.mainType;
|
|
var queryCond = getQueryCond(query);
|
var result = queryCond
|
? this.queryComponents(queryCond)
|
: this._componentsMap[mainType];
|
|
return doFilter(filterBySubType(result, condition));
|
|
function getQueryCond(q) {
|
var indexAttr = mainType + 'Index';
|
var idAttr = mainType + 'Id';
|
var nameAttr = mainType + 'Name';
|
return q && (
|
q[indexAttr] != null
|
|| q[idAttr] != null
|
|| q[nameAttr] != null
|
)
|
? {
|
mainType: mainType,
|
// subType will be filtered finally.
|
index: q[indexAttr],
|
id: q[idAttr],
|
name: q[nameAttr]
|
}
|
: null;
|
}
|
|
function doFilter(res) {
|
return condition.filter
|
? filter(res, condition.filter)
|
: res;
|
}
|
},
|
|
/**
|
* @usage
|
* eachComponent('legend', function (legendModel, index) {
|
* ...
|
* });
|
* eachComponent(function (componentType, model, index) {
|
* // componentType does not include subType
|
* // (componentType is 'xxx' but not 'xxx.aa')
|
* });
|
* eachComponent(
|
* {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},
|
* function (model, index) {...}
|
* );
|
* eachComponent(
|
* {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},
|
* function (model, index) {...}
|
* );
|
*
|
* @param {string|Object=} mainType When mainType is object, the definition
|
* is the same as the method 'findComponents'.
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
eachComponent: function (mainType, cb, context) {
|
var componentsMap = this._componentsMap;
|
|
if (typeof mainType === 'function') {
|
context = cb;
|
cb = mainType;
|
each(componentsMap, function (components, componentType) {
|
each(components, function (component, index) {
|
cb.call(context, componentType, component, index);
|
});
|
});
|
}
|
else if (zrUtil.isString(mainType)) {
|
each(componentsMap[mainType], cb, context);
|
}
|
else if (isObject(mainType)) {
|
var queryResult = this.findComponents(mainType);
|
each(queryResult, cb, context);
|
}
|
},
|
|
/**
|
* @param {string} name
|
* @return {Array.<module:echarts/model/Series>}
|
*/
|
getSeriesByName: function (name) {
|
var series = this._componentsMap.series;
|
return filter(series, function (oneSeries) {
|
return oneSeries.name === name;
|
});
|
},
|
|
/**
|
* @param {number} seriesIndex
|
* @return {module:echarts/model/Series}
|
*/
|
getSeriesByIndex: function (seriesIndex) {
|
return this._componentsMap.series[seriesIndex];
|
},
|
|
/**
|
* @param {string} subType
|
* @return {Array.<module:echarts/model/Series>}
|
*/
|
getSeriesByType: function (subType) {
|
var series = this._componentsMap.series;
|
return filter(series, function (oneSeries) {
|
return oneSeries.subType === subType;
|
});
|
},
|
|
/**
|
* @return {Array.<module:echarts/model/Series>}
|
*/
|
getSeries: function () {
|
return this._componentsMap.series.slice();
|
},
|
|
/**
|
* After filtering, series may be different
|
* frome raw series.
|
*
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
eachSeries: function (cb, context) {
|
assertSeriesInitialized(this);
|
each(this._seriesIndices, function (rawSeriesIndex) {
|
var series = this._componentsMap.series[rawSeriesIndex];
|
cb.call(context, series, rawSeriesIndex);
|
}, this);
|
},
|
|
/**
|
* Iterate raw series before filtered.
|
*
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
eachRawSeries: function (cb, context) {
|
each(this._componentsMap.series, cb, context);
|
},
|
|
/**
|
* After filtering, series may be different.
|
* frome raw series.
|
*
|
* @parma {string} subType
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
eachSeriesByType: function (subType, cb, context) {
|
assertSeriesInitialized(this);
|
each(this._seriesIndices, function (rawSeriesIndex) {
|
var series = this._componentsMap.series[rawSeriesIndex];
|
if (series.subType === subType) {
|
cb.call(context, series, rawSeriesIndex);
|
}
|
}, this);
|
},
|
|
/**
|
* Iterate raw series before filtered of given type.
|
*
|
* @parma {string} subType
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
eachRawSeriesByType: function (subType, cb, context) {
|
return each(this.getSeriesByType(subType), cb, context);
|
},
|
|
/**
|
* @param {module:echarts/model/Series} seriesModel
|
*/
|
isSeriesFiltered: function (seriesModel) {
|
assertSeriesInitialized(this);
|
return zrUtil.indexOf(this._seriesIndices, seriesModel.componentIndex) < 0;
|
},
|
|
/**
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
filterSeries: function (cb, context) {
|
assertSeriesInitialized(this);
|
var filteredSeries = filter(
|
this._componentsMap.series, cb, context
|
);
|
this._seriesIndices = createSeriesIndices(filteredSeries);
|
},
|
|
restoreData: function () {
|
var componentsMap = this._componentsMap;
|
|
this._seriesIndices = createSeriesIndices(componentsMap.series);
|
|
var componentTypes = [];
|
each(componentsMap, function (components, componentType) {
|
componentTypes.push(componentType);
|
});
|
|
ComponentModel.topologicalTravel(
|
componentTypes,
|
ComponentModel.getAllClassMainTypes(),
|
function (componentType, dependencies) {
|
each(componentsMap[componentType], function (component) {
|
component.restoreData();
|
});
|
}
|
);
|
}
|
|
});
|
|
/**
|
* @inner
|
*/
|
function mergeTheme(option, theme) {
|
zrUtil.each(theme, function (themeItem, name) {
|
// 如果有 component model 则把具体的 merge 逻辑交给该 model 处理
|
if (!ComponentModel.hasClass(name)) {
|
if (typeof themeItem === 'object') {
|
option[name] = !option[name]
|
? zrUtil.clone(themeItem)
|
: zrUtil.merge(option[name], themeItem, false);
|
}
|
else {
|
if (option[name] == null) {
|
option[name] = themeItem;
|
}
|
}
|
}
|
});
|
}
|
|
function initBase(baseOption) {
|
baseOption = baseOption;
|
|
// Using OPTION_INNER_KEY to mark that this option can not be used outside,
|
// i.e. `chart.setOption(chart.getModel().option);` is forbiden.
|
this.option = {};
|
this.option[OPTION_INNER_KEY] = 1;
|
|
/**
|
* Init with series: [], in case of calling findSeries method
|
* before series initialized.
|
* @type {Object.<string, Array.<module:echarts/model/Model>>}
|
* @private
|
*/
|
this._componentsMap = {series: []};
|
|
/**
|
* Mapping between filtered series list and raw series list.
|
* key: filtered series indices, value: raw series indices.
|
* @type {Array.<nubmer>}
|
* @private
|
*/
|
this._seriesIndices = null;
|
|
mergeTheme(baseOption, this._theme.option);
|
|
// TODO Needs clone when merging to the unexisted property
|
zrUtil.merge(baseOption, globalDefault, false);
|
|
this.mergeOption(baseOption);
|
}
|
|
/**
|
* @inner
|
* @param {Array.<string>|string} types model types
|
* @return {Object} key: {string} type, value: {Array.<Object>} models
|
*/
|
function getComponentsByTypes(componentsMap, types) {
|
if (!zrUtil.isArray(types)) {
|
types = types ? [types] : [];
|
}
|
|
var ret = {};
|
each(types, function (type) {
|
ret[type] = (componentsMap[type] || []).slice();
|
});
|
|
return ret;
|
}
|
|
/**
|
* @inner
|
*/
|
function determineSubType(mainType, newCptOption, existComponent) {
|
var subType = newCptOption.type
|
? newCptOption.type
|
: existComponent
|
? existComponent.subType
|
// Use determineSubType only when there is no existComponent.
|
: ComponentModel.determineSubType(mainType, newCptOption);
|
|
// tooltip, markline, markpoint may always has no subType
|
return subType;
|
}
|
|
/**
|
* @inner
|
*/
|
function createSeriesIndices(seriesModels) {
|
return map(seriesModels, function (series) {
|
return series.componentIndex;
|
}) || [];
|
}
|
|
/**
|
* @inner
|
*/
|
function filterBySubType(components, condition) {
|
// Using hasOwnProperty for restrict. Consider
|
// subType is undefined in user payload.
|
return condition.hasOwnProperty('subType')
|
? filter(components, function (cpt) {
|
return cpt.subType === condition.subType;
|
})
|
: components;
|
}
|
|
/**
|
* @inner
|
*/
|
function assertSeriesInitialized(ecModel) {
|
// Components that use _seriesIndices should depends on series component,
|
// which make sure that their initialization is after series.
|
if (true) {
|
if (!ecModel._seriesIndices) {
|
throw new Error('Series has not been initialized yet.');
|
}
|
}
|
}
|
|
zrUtil.mixin(GlobalModel, __webpack_require__(24));
|
|
module.exports = GlobalModel;
|
|
|
/***/ },
|
/* 4 */
|
/***/ function(module, exports) {
|
|
/**
|
* @module zrender/core/util
|
*/
|
|
|
// 用于处理merge时无法遍历Date等对象的问题
|
var BUILTIN_OBJECT = {
|
'[object Function]': 1,
|
'[object RegExp]': 1,
|
'[object Date]': 1,
|
'[object Error]': 1,
|
'[object CanvasGradient]': 1,
|
'[object CanvasPattern]': 1,
|
// For node-canvas
|
'[object Image]': 1,
|
'[object Canvas]': 1
|
};
|
|
var TYPED_ARRAY = {
|
'[object Int8Array]': 1,
|
'[object Uint8Array]': 1,
|
'[object Uint8ClampedArray]': 1,
|
'[object Int16Array]': 1,
|
'[object Uint16Array]': 1,
|
'[object Int32Array]': 1,
|
'[object Uint32Array]': 1,
|
'[object Float32Array]': 1,
|
'[object Float64Array]': 1
|
};
|
|
var objToString = Object.prototype.toString;
|
|
var arrayProto = Array.prototype;
|
var nativeForEach = arrayProto.forEach;
|
var nativeFilter = arrayProto.filter;
|
var nativeSlice = arrayProto.slice;
|
var nativeMap = arrayProto.map;
|
var nativeReduce = arrayProto.reduce;
|
|
/**
|
* Those data types can be cloned:
|
* Plain object, Array, TypedArray, number, string, null, undefined.
|
* Those data types will be assgined using the orginal data:
|
* BUILTIN_OBJECT
|
* Instance of user defined class will be cloned to a plain object, without
|
* properties in prototype.
|
* Other data types is not supported (not sure what will happen).
|
*
|
* Caution: do not support clone Date, for performance consideration.
|
* (There might be a large number of date in `series.data`).
|
* So date should not be modified in and out of echarts.
|
*
|
* @param {*} source
|
* @return {*} new
|
*/
|
function clone(source) {
|
if (source == null || typeof source != 'object') {
|
return source;
|
}
|
|
var result = source;
|
var typeStr = objToString.call(source);
|
|
if (typeStr === '[object Array]') {
|
result = [];
|
for (var i = 0, len = source.length; i < len; i++) {
|
result[i] = clone(source[i]);
|
}
|
}
|
else if (TYPED_ARRAY[typeStr]) {
|
result = source.constructor.from(source);
|
}
|
else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
|
result = {};
|
for (var key in source) {
|
if (source.hasOwnProperty(key)) {
|
result[key] = clone(source[key]);
|
}
|
}
|
}
|
|
return result;
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} target
|
* @param {*} source
|
* @param {boolean} [overwrite=false]
|
*/
|
function merge(target, source, overwrite) {
|
// We should escapse that source is string
|
// and enter for ... in ...
|
if (!isObject(source) || !isObject(target)) {
|
return overwrite ? clone(source) : target;
|
}
|
|
for (var key in source) {
|
if (source.hasOwnProperty(key)) {
|
var targetProp = target[key];
|
var sourceProp = source[key];
|
|
if (isObject(sourceProp)
|
&& isObject(targetProp)
|
&& !isArray(sourceProp)
|
&& !isArray(targetProp)
|
&& !isDom(sourceProp)
|
&& !isDom(targetProp)
|
&& !isBuiltInObject(sourceProp)
|
&& !isBuiltInObject(targetProp)
|
&& !isPrimitive(sourceProp)
|
&& !isPrimitive(targetProp)
|
) {
|
// 如果需要递归覆盖,就递归调用merge
|
merge(targetProp, sourceProp, overwrite);
|
}
|
else if (overwrite || !(key in target)) {
|
// 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
|
// NOTE,在 target[key] 不存在的时候也是直接覆盖
|
target[key] = clone(source[key], true);
|
}
|
}
|
}
|
|
return target;
|
}
|
|
/**
|
* @param {Array} targetAndSources The first item is target, and the rests are source.
|
* @param {boolean} [overwrite=false]
|
* @return {*} target
|
*/
|
function mergeAll(targetAndSources, overwrite) {
|
var result = targetAndSources[0];
|
for (var i = 1, len = targetAndSources.length; i < len; i++) {
|
result = merge(result, targetAndSources[i], overwrite);
|
}
|
return result;
|
}
|
|
/**
|
* @param {*} target
|
* @param {*} source
|
* @memberOf module:zrender/core/util
|
*/
|
function extend(target, source) {
|
for (var key in source) {
|
if (source.hasOwnProperty(key)) {
|
target[key] = source[key];
|
}
|
}
|
return target;
|
}
|
|
/**
|
* @param {*} target
|
* @param {*} source
|
* @param {boolen} [overlay=false]
|
* @memberOf module:zrender/core/util
|
*/
|
function defaults(target, source, overlay) {
|
for (var key in source) {
|
if (source.hasOwnProperty(key)
|
&& (overlay ? source[key] != null : target[key] == null)
|
) {
|
target[key] = source[key];
|
}
|
}
|
return target;
|
}
|
|
function createCanvas() {
|
return document.createElement('canvas');
|
}
|
// FIXME
|
var _ctx;
|
function getContext() {
|
if (!_ctx) {
|
// Use util.createCanvas instead of createCanvas
|
// because createCanvas may be overwritten in different environment
|
_ctx = util.createCanvas().getContext('2d');
|
}
|
return _ctx;
|
}
|
|
/**
|
* 查询数组中元素的index
|
* @memberOf module:zrender/core/util
|
*/
|
function indexOf(array, value) {
|
if (array) {
|
if (array.indexOf) {
|
return array.indexOf(value);
|
}
|
for (var i = 0, len = array.length; i < len; i++) {
|
if (array[i] === value) {
|
return i;
|
}
|
}
|
}
|
return -1;
|
}
|
|
/**
|
* 构造类继承关系
|
*
|
* @memberOf module:zrender/core/util
|
* @param {Function} clazz 源类
|
* @param {Function} baseClazz 基类
|
*/
|
function inherits(clazz, baseClazz) {
|
var clazzPrototype = clazz.prototype;
|
function F() {}
|
F.prototype = baseClazz.prototype;
|
clazz.prototype = new F();
|
|
for (var prop in clazzPrototype) {
|
clazz.prototype[prop] = clazzPrototype[prop];
|
}
|
clazz.prototype.constructor = clazz;
|
clazz.superClass = baseClazz;
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {Object|Function} target
|
* @param {Object|Function} sorce
|
* @param {boolean} overlay
|
*/
|
function mixin(target, source, overlay) {
|
target = 'prototype' in target ? target.prototype : target;
|
source = 'prototype' in source ? source.prototype : source;
|
|
defaults(target, source, overlay);
|
}
|
|
/**
|
* Consider typed array.
|
* @param {Array|TypedArray} data
|
*/
|
function isArrayLike(data) {
|
if (! data) {
|
return;
|
}
|
if (typeof data == 'string') {
|
return false;
|
}
|
return typeof data.length == 'number';
|
}
|
|
/**
|
* 数组或对象遍历
|
* @memberOf module:zrender/core/util
|
* @param {Object|Array} obj
|
* @param {Function} cb
|
* @param {*} [context]
|
*/
|
function each(obj, cb, context) {
|
if (!(obj && cb)) {
|
return;
|
}
|
if (obj.forEach && obj.forEach === nativeForEach) {
|
obj.forEach(cb, context);
|
}
|
else if (obj.length === +obj.length) {
|
for (var i = 0, len = obj.length; i < len; i++) {
|
cb.call(context, obj[i], i, obj);
|
}
|
}
|
else {
|
for (var key in obj) {
|
if (obj.hasOwnProperty(key)) {
|
cb.call(context, obj[key], key, obj);
|
}
|
}
|
}
|
}
|
|
/**
|
* 数组映射
|
* @memberOf module:zrender/core/util
|
* @param {Array} obj
|
* @param {Function} cb
|
* @param {*} [context]
|
* @return {Array}
|
*/
|
function map(obj, cb, context) {
|
if (!(obj && cb)) {
|
return;
|
}
|
if (obj.map && obj.map === nativeMap) {
|
return obj.map(cb, context);
|
}
|
else {
|
var result = [];
|
for (var i = 0, len = obj.length; i < len; i++) {
|
result.push(cb.call(context, obj[i], i, obj));
|
}
|
return result;
|
}
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {Array} obj
|
* @param {Function} cb
|
* @param {Object} [memo]
|
* @param {*} [context]
|
* @return {Array}
|
*/
|
function reduce(obj, cb, memo, context) {
|
if (!(obj && cb)) {
|
return;
|
}
|
if (obj.reduce && obj.reduce === nativeReduce) {
|
return obj.reduce(cb, memo, context);
|
}
|
else {
|
for (var i = 0, len = obj.length; i < len; i++) {
|
memo = cb.call(context, memo, obj[i], i, obj);
|
}
|
return memo;
|
}
|
}
|
|
/**
|
* 数组过滤
|
* @memberOf module:zrender/core/util
|
* @param {Array} obj
|
* @param {Function} cb
|
* @param {*} [context]
|
* @return {Array}
|
*/
|
function filter(obj, cb, context) {
|
if (!(obj && cb)) {
|
return;
|
}
|
if (obj.filter && obj.filter === nativeFilter) {
|
return obj.filter(cb, context);
|
}
|
else {
|
var result = [];
|
for (var i = 0, len = obj.length; i < len; i++) {
|
if (cb.call(context, obj[i], i, obj)) {
|
result.push(obj[i]);
|
}
|
}
|
return result;
|
}
|
}
|
|
/**
|
* 数组项查找
|
* @memberOf module:zrender/core/util
|
* @param {Array} obj
|
* @param {Function} cb
|
* @param {*} [context]
|
* @return {Array}
|
*/
|
function find(obj, cb, context) {
|
if (!(obj && cb)) {
|
return;
|
}
|
for (var i = 0, len = obj.length; i < len; i++) {
|
if (cb.call(context, obj[i], i, obj)) {
|
return obj[i];
|
}
|
}
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {Function} func
|
* @param {*} context
|
* @return {Function}
|
*/
|
function bind(func, context) {
|
var args = nativeSlice.call(arguments, 2);
|
return function () {
|
return func.apply(context, args.concat(nativeSlice.call(arguments)));
|
};
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {Function} func
|
* @return {Function}
|
*/
|
function curry(func) {
|
var args = nativeSlice.call(arguments, 1);
|
return function () {
|
return func.apply(this, args.concat(nativeSlice.call(arguments)));
|
};
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isArray(value) {
|
return objToString.call(value) === '[object Array]';
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isFunction(value) {
|
return typeof value === 'function';
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isString(value) {
|
return objToString.call(value) === '[object String]';
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isObject(value) {
|
// Avoid a V8 JIT bug in Chrome 19-20.
|
// See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
|
var type = typeof value;
|
return type === 'function' || (!!value && type == 'object');
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isBuiltInObject(value) {
|
return !!BUILTIN_OBJECT[objToString.call(value)];
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function isDom(value) {
|
return typeof value === 'object'
|
&& typeof value.nodeType === 'number'
|
&& typeof value.ownerDocument === 'object';
|
}
|
|
/**
|
* Whether is exactly NaN. Notice isNaN('a') returns true.
|
* @param {*} value
|
* @return {boolean}
|
*/
|
function eqNaN(value) {
|
return value !== value;
|
}
|
|
/**
|
* If value1 is not null, then return value1, otherwise judget rest of values.
|
* @memberOf module:zrender/core/util
|
* @return {*} Final value
|
*/
|
function retrieve(values) {
|
for (var i = 0, len = arguments.length; i < len; i++) {
|
if (arguments[i] != null) {
|
return arguments[i];
|
}
|
}
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {Array} arr
|
* @param {number} startIndex
|
* @param {number} endIndex
|
* @return {Array}
|
*/
|
function slice() {
|
return Function.call.apply(nativeSlice, arguments);
|
}
|
|
/**
|
* @memberOf module:zrender/core/util
|
* @param {boolean} condition
|
* @param {string} message
|
*/
|
function assert(condition, message) {
|
if (!condition) {
|
throw new Error(message);
|
}
|
}
|
|
var primitiveKey = '__ec_primitive__';
|
/**
|
* Set an object as primitive to be ignored traversing children in clone or merge
|
*/
|
function setAsPrimitive(obj) {
|
obj[primitiveKey] = true;
|
}
|
|
function isPrimitive(obj) {
|
return obj[primitiveKey];
|
}
|
|
/**
|
* @constructor
|
*/
|
function HashMap(obj) {
|
obj && extend(this, obj);
|
}
|
|
// Add prefix to avoid conflict with Object.prototype.
|
var HASH_MAP_PREFIX = '_ec_';
|
var HASH_MAP_PREFIX_LENGTH = 4;
|
|
HashMap.prototype = {
|
constructor: HashMap,
|
// Do not provide `has` method to avoid defining what is `has`.
|
// (We usually treat `null` and `undefined` as the same, different
|
// from ES6 Map).
|
get: function (key) {
|
return this[HASH_MAP_PREFIX + key];
|
},
|
set: function (key, value) {
|
this[HASH_MAP_PREFIX + key] = value;
|
// Comparing with invocation chaining, `return value` is more commonly
|
// used in this case: `var someVal = map.set('a', genVal());`
|
return value;
|
},
|
// Although util.each can be performed on this hashMap directly, user
|
// should not use the exposed keys, who are prefixed.
|
each: function (cb, context) {
|
context !== void 0 && (cb = bind(cb, context));
|
for (var prefixedKey in this) {
|
this.hasOwnProperty(prefixedKey)
|
&& cb(this[prefixedKey], prefixedKey.slice(HASH_MAP_PREFIX_LENGTH));
|
}
|
},
|
// Do not use this method if performance sensitive.
|
removeKey: function (key) {
|
delete this[key];
|
}
|
};
|
|
function createHashMap() {
|
return new HashMap();
|
}
|
|
var util = {
|
inherits: inherits,
|
mixin: mixin,
|
clone: clone,
|
merge: merge,
|
mergeAll: mergeAll,
|
extend: extend,
|
defaults: defaults,
|
getContext: getContext,
|
createCanvas: createCanvas,
|
indexOf: indexOf,
|
slice: slice,
|
find: find,
|
isArrayLike: isArrayLike,
|
each: each,
|
map: map,
|
reduce: reduce,
|
filter: filter,
|
bind: bind,
|
curry: curry,
|
isArray: isArray,
|
isString: isString,
|
isObject: isObject,
|
isFunction: isFunction,
|
isBuiltInObject: isBuiltInObject,
|
isDom: isDom,
|
eqNaN: eqNaN,
|
retrieve: retrieve,
|
assert: assert,
|
setAsPrimitive: setAsPrimitive,
|
createHashMap: createHashMap,
|
noop: function () {}
|
};
|
module.exports = util;
|
|
|
|
/***/ },
|
/* 5 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var formatUtil = __webpack_require__(6);
|
var nubmerUtil = __webpack_require__(7);
|
var Model = __webpack_require__(12);
|
var zrUtil = __webpack_require__(4);
|
var each = zrUtil.each;
|
var isObject = zrUtil.isObject;
|
|
var modelUtil = {};
|
|
/**
|
* If value is not array, then translate it to array.
|
* @param {*} value
|
* @return {Array} [value] or value
|
*/
|
modelUtil.normalizeToArray = function (value) {
|
return value instanceof Array
|
? value
|
: value == null
|
? []
|
: [value];
|
};
|
|
/**
|
* Sync default option between normal and emphasis like `position` and `show`
|
* In case some one will write code like
|
* label: {
|
* normal: {
|
* show: false,
|
* position: 'outside',
|
* textStyle: {
|
* fontSize: 18
|
* }
|
* },
|
* emphasis: {
|
* show: true
|
* }
|
* }
|
* @param {Object} opt
|
* @param {Array.<string>} subOpts
|
*/
|
modelUtil.defaultEmphasis = function (opt, subOpts) {
|
if (opt) {
|
var emphasisOpt = opt.emphasis = opt.emphasis || {};
|
var normalOpt = opt.normal = opt.normal || {};
|
|
// Default emphasis option from normal
|
each(subOpts, function (subOptName) {
|
var val = zrUtil.retrieve(emphasisOpt[subOptName], normalOpt[subOptName]);
|
if (val != null) {
|
emphasisOpt[subOptName] = val;
|
}
|
});
|
}
|
};
|
|
modelUtil.LABEL_OPTIONS = ['position', 'offset', 'show', 'textStyle', 'distance', 'formatter'];
|
|
/**
|
* data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
|
* This helper method retieves value from data.
|
* @param {string|number|Date|Array|Object} dataItem
|
* @return {number|string|Date|Array.<number|string|Date>}
|
*/
|
modelUtil.getDataItemValue = function (dataItem) {
|
// Performance sensitive.
|
return dataItem && (dataItem.value == null ? dataItem : dataItem.value);
|
};
|
|
/**
|
* data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]
|
* This helper method determine if dataItem has extra option besides value
|
* @param {string|number|Date|Array|Object} dataItem
|
*/
|
modelUtil.isDataItemOption = function (dataItem) {
|
return isObject(dataItem)
|
&& !(dataItem instanceof Array);
|
// // markLine data can be array
|
// && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
|
};
|
|
/**
|
* This helper method convert value in data.
|
* @param {string|number|Date} value
|
* @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
|
*/
|
modelUtil.converDataValue = function (value, dimInfo) {
|
// Performance sensitive.
|
var dimType = dimInfo && dimInfo.type;
|
if (dimType === 'ordinal') {
|
return value;
|
}
|
|
if (dimType === 'time'
|
// spead up when using timestamp
|
&& typeof value !== 'number'
|
&& value != null
|
&& value !== '-'
|
) {
|
value = +nubmerUtil.parseDate(value);
|
}
|
|
// dimType defaults 'number'.
|
// If dimType is not ordinal and value is null or undefined or NaN or '-',
|
// parse to NaN.
|
return (value == null || value === '')
|
? NaN : +value; // If string (like '-'), using '+' parse to NaN
|
};
|
|
/**
|
* Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data.
|
* @param {module:echarts/data/List} data
|
* @param {Object} opt
|
* @param {string} [opt.seriesIndex]
|
* @param {Object} [opt.name]
|
* @param {Object} [opt.mainType]
|
* @param {Object} [opt.subType]
|
*/
|
modelUtil.createDataFormatModel = function (data, opt) {
|
var model = new Model();
|
zrUtil.mixin(model, modelUtil.dataFormatMixin);
|
model.seriesIndex = opt.seriesIndex;
|
model.name = opt.name || '';
|
model.mainType = opt.mainType;
|
model.subType = opt.subType;
|
|
model.getData = function () {
|
return data;
|
};
|
return model;
|
};
|
|
// PENDING A little ugly
|
modelUtil.dataFormatMixin = {
|
/**
|
* Get params for formatter
|
* @param {number} dataIndex
|
* @param {string} [dataType]
|
* @return {Object}
|
*/
|
getDataParams: function (dataIndex, dataType) {
|
var data = this.getData(dataType);
|
var rawValue = this.getRawValue(dataIndex, dataType);
|
var rawDataIndex = data.getRawIndex(dataIndex);
|
var name = data.getName(dataIndex, true);
|
var itemOpt = data.getRawDataItem(dataIndex);
|
|
return {
|
componentType: this.mainType,
|
componentSubType: this.subType,
|
seriesType: this.mainType === 'series' ? this.subType : null,
|
seriesIndex: this.seriesIndex,
|
seriesId: this.id,
|
seriesName: this.name,
|
name: name,
|
dataIndex: rawDataIndex,
|
data: itemOpt,
|
dataType: dataType,
|
value: rawValue,
|
color: data.getItemVisual(dataIndex, 'color'),
|
|
// Param name list for mapping `a`, `b`, `c`, `d`, `e`
|
$vars: ['seriesName', 'name', 'value']
|
};
|
},
|
|
/**
|
* Format label
|
* @param {number} dataIndex
|
* @param {string} [status='normal'] 'normal' or 'emphasis'
|
* @param {string} [dataType]
|
* @param {number} [dimIndex]
|
* @return {string}
|
*/
|
getFormattedLabel: function (dataIndex, status, dataType, dimIndex) {
|
status = status || 'normal';
|
var data = this.getData(dataType);
|
var itemModel = data.getItemModel(dataIndex);
|
|
var params = this.getDataParams(dataIndex, dataType);
|
if (dimIndex != null && (params.value instanceof Array)) {
|
params.value = params.value[dimIndex];
|
}
|
|
var formatter = itemModel.get(['label', status, 'formatter']);
|
|
if (typeof formatter === 'function') {
|
params.status = status;
|
return formatter(params);
|
}
|
else if (typeof formatter === 'string') {
|
return formatUtil.formatTpl(formatter, params);
|
}
|
},
|
|
/**
|
* Get raw value in option
|
* @param {number} idx
|
* @param {string} [dataType]
|
* @return {Object}
|
*/
|
getRawValue: function (idx, dataType) {
|
var data = this.getData(dataType);
|
var dataItem = data.getRawDataItem(idx);
|
if (dataItem != null) {
|
return (isObject(dataItem) && !(dataItem instanceof Array))
|
? dataItem.value : dataItem;
|
}
|
},
|
|
/**
|
* Should be implemented.
|
* @param {number} dataIndex
|
* @param {boolean} [multipleSeries=false]
|
* @param {number} [dataType]
|
* @return {string} tooltip string
|
*/
|
formatTooltip: zrUtil.noop
|
};
|
|
/**
|
* Mapping to exists for merge.
|
*
|
* @public
|
* @param {Array.<Object>|Array.<module:echarts/model/Component>} exists
|
* @param {Object|Array.<Object>} newCptOptions
|
* @return {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
|
* index of which is the same as exists.
|
*/
|
modelUtil.mappingToExists = function (exists, newCptOptions) {
|
// Mapping by the order by original option (but not order of
|
// new option) in merge mode. Because we should ensure
|
// some specified index (like xAxisIndex) is consistent with
|
// original option, which is easy to understand, espatially in
|
// media query. And in most case, merge option is used to
|
// update partial option but not be expected to change order.
|
newCptOptions = (newCptOptions || []).slice();
|
|
var result = zrUtil.map(exists || [], function (obj, index) {
|
return {exist: obj};
|
});
|
|
// Mapping by id or name if specified.
|
each(newCptOptions, function (cptOption, index) {
|
if (!isObject(cptOption)) {
|
return;
|
}
|
|
// id has highest priority.
|
for (var i = 0; i < result.length; i++) {
|
if (!result[i].option // Consider name: two map to one.
|
&& cptOption.id != null
|
&& result[i].exist.id === cptOption.id + ''
|
) {
|
result[i].option = cptOption;
|
newCptOptions[index] = null;
|
return;
|
}
|
}
|
|
for (var i = 0; i < result.length; i++) {
|
var exist = result[i].exist;
|
if (!result[i].option // Consider name: two map to one.
|
// Can not match when both ids exist but different.
|
&& (exist.id == null || cptOption.id == null)
|
&& cptOption.name != null
|
&& !modelUtil.isIdInner(cptOption)
|
&& !modelUtil.isIdInner(exist)
|
&& exist.name === cptOption.name + ''
|
) {
|
result[i].option = cptOption;
|
newCptOptions[index] = null;
|
return;
|
}
|
}
|
});
|
|
// Otherwise mapping by index.
|
each(newCptOptions, function (cptOption, index) {
|
if (!isObject(cptOption)) {
|
return;
|
}
|
|
var i = 0;
|
for (; i < result.length; i++) {
|
var exist = result[i].exist;
|
if (!result[i].option
|
// Existing model that already has id should be able to
|
// mapped to (because after mapping performed model may
|
// be assigned with a id, whish should not affect next
|
// mapping), except those has inner id.
|
&& !modelUtil.isIdInner(exist)
|
// Caution:
|
// Do not overwrite id. But name can be overwritten,
|
// because axis use name as 'show label text'.
|
// 'exist' always has id and name and we dont
|
// need to check it.
|
&& cptOption.id == null
|
) {
|
result[i].option = cptOption;
|
break;
|
}
|
}
|
|
if (i >= result.length) {
|
result.push({option: cptOption});
|
}
|
});
|
|
return result;
|
};
|
|
/**
|
* Make id and name for mapping result (result of mappingToExists)
|
* into `keyInfo` field.
|
*
|
* @public
|
* @param {Array.<Object>} Result, like [{exist: ..., option: ...}, {}],
|
* which order is the same as exists.
|
* @return {Array.<Object>} The input.
|
*/
|
modelUtil.makeIdAndName = function (mapResult) {
|
// We use this id to hash component models and view instances
|
// in echarts. id can be specified by user, or auto generated.
|
|
// The id generation rule ensures new view instance are able
|
// to mapped to old instance when setOption are called in
|
// no-merge mode. So we generate model id by name and plus
|
// type in view id.
|
|
// name can be duplicated among components, which is convenient
|
// to specify multi components (like series) by one name.
|
|
// Ensure that each id is distinct.
|
var idMap = {};
|
|
each(mapResult, function (item, index) {
|
var existCpt = item.exist;
|
existCpt && (idMap[existCpt.id] = item);
|
});
|
|
each(mapResult, function (item, index) {
|
var opt = item.option;
|
|
zrUtil.assert(
|
!opt || opt.id == null || !idMap[opt.id] || idMap[opt.id] === item,
|
'id duplicates: ' + (opt && opt.id)
|
);
|
|
opt && opt.id != null && (idMap[opt.id] = item);
|
!item.keyInfo && (item.keyInfo = {});
|
});
|
|
// Make name and id.
|
each(mapResult, function (item, index) {
|
var existCpt = item.exist;
|
var opt = item.option;
|
var keyInfo = item.keyInfo;
|
|
if (!isObject(opt)) {
|
return;
|
}
|
|
// name can be overwitten. Consider case: axis.name = '20km'.
|
// But id generated by name will not be changed, which affect
|
// only in that case: setOption with 'not merge mode' and view
|
// instance will be recreated, which can be accepted.
|
keyInfo.name = opt.name != null
|
? opt.name + ''
|
: existCpt
|
? existCpt.name
|
: '\0-'; // name may be displayed on screen, so use '-'.
|
|
if (existCpt) {
|
keyInfo.id = existCpt.id;
|
}
|
else if (opt.id != null) {
|
keyInfo.id = opt.id + '';
|
}
|
else {
|
// Consider this situatoin:
|
// optionA: [{name: 'a'}, {name: 'a'}, {..}]
|
// optionB [{..}, {name: 'a'}, {name: 'a'}]
|
// Series with the same name between optionA and optionB
|
// should be mapped.
|
var idNum = 0;
|
do {
|
keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++;
|
}
|
while (idMap[keyInfo.id]);
|
}
|
|
idMap[keyInfo.id] = item;
|
});
|
};
|
|
/**
|
* @public
|
* @param {Object} cptOption
|
* @return {boolean}
|
*/
|
modelUtil.isIdInner = function (cptOption) {
|
return isObject(cptOption)
|
&& cptOption.id
|
&& (cptOption.id + '').indexOf('\0_ec_\0') === 0;
|
};
|
|
/**
|
* A helper for removing duplicate items between batchA and batchB,
|
* and in themselves, and categorize by series.
|
*
|
* @param {Array.<Object>} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
|
* @param {Array.<Object>} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]
|
* @return {Array.<Array.<Object>, Array.<Object>>} result: [resultBatchA, resultBatchB]
|
*/
|
modelUtil.compressBatches = function (batchA, batchB) {
|
var mapA = {};
|
var mapB = {};
|
|
makeMap(batchA || [], mapA);
|
makeMap(batchB || [], mapB, mapA);
|
|
return [mapToArray(mapA), mapToArray(mapB)];
|
|
function makeMap(sourceBatch, map, otherMap) {
|
for (var i = 0, len = sourceBatch.length; i < len; i++) {
|
var seriesId = sourceBatch[i].seriesId;
|
var dataIndices = modelUtil.normalizeToArray(sourceBatch[i].dataIndex);
|
var otherDataIndices = otherMap && otherMap[seriesId];
|
|
for (var j = 0, lenj = dataIndices.length; j < lenj; j++) {
|
var dataIndex = dataIndices[j];
|
|
if (otherDataIndices && otherDataIndices[dataIndex]) {
|
otherDataIndices[dataIndex] = null;
|
}
|
else {
|
(map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1;
|
}
|
}
|
}
|
}
|
|
function mapToArray(map, isData) {
|
var result = [];
|
for (var i in map) {
|
if (map.hasOwnProperty(i) && map[i] != null) {
|
if (isData) {
|
result.push(+i);
|
}
|
else {
|
var dataIndices = mapToArray(map[i], true);
|
dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices});
|
}
|
}
|
}
|
return result;
|
}
|
};
|
|
/**
|
* @param {module:echarts/data/List} data
|
* @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name
|
* each of which can be Array or primary type.
|
* @return {number|Array.<number>} dataIndex If not found, return undefined/null.
|
*/
|
modelUtil.queryDataIndex = function (data, payload) {
|
if (payload.dataIndexInside != null) {
|
return payload.dataIndexInside;
|
}
|
else if (payload.dataIndex != null) {
|
return zrUtil.isArray(payload.dataIndex)
|
? zrUtil.map(payload.dataIndex, function (value) {
|
return data.indexOfRawIndex(value);
|
})
|
: data.indexOfRawIndex(payload.dataIndex);
|
}
|
else if (payload.name != null) {
|
return zrUtil.isArray(payload.name)
|
? zrUtil.map(payload.name, function (value) {
|
return data.indexOfName(value);
|
})
|
: data.indexOfName(payload.name);
|
}
|
};
|
|
/**
|
* Enable property storage to any host object.
|
* Notice: Serialization is not supported.
|
*
|
* For example:
|
* var get = modelUitl.makeGetter();
|
*
|
* function some(hostObj) {
|
* get(hostObj)._someProperty = 1212;
|
* ...
|
* }
|
*
|
* @return {Function}
|
*/
|
modelUtil.makeGetter = (function () {
|
var index = 0;
|
return function () {
|
var key = '\0__ec_prop_getter_' + index++;
|
return function (hostObj) {
|
return hostObj[key] || (hostObj[key] = {});
|
};
|
};
|
})();
|
|
/**
|
* @param {module:echarts/model/Global} ecModel
|
* @param {string|Object} finder
|
* If string, e.g., 'geo', means {geoIndex: 0}.
|
* If Object, could contain some of these properties below:
|
* {
|
* seriesIndex, seriesId, seriesName,
|
* geoIndex, geoId, geoName,
|
* bmapIndex, bmapId, bmapName,
|
* xAxisIndex, xAxisId, xAxisName,
|
* yAxisIndex, yAxisId, yAxisName,
|
* gridIndex, gridId, gridName,
|
* ... (can be extended)
|
* }
|
* Each properties can be number|string|Array.<number>|Array.<string>
|
* For example, a finder could be
|
* {
|
* seriesIndex: 3,
|
* geoId: ['aa', 'cc'],
|
* gridName: ['xx', 'rr']
|
* }
|
* xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)
|
* If nothing or null/undefined specified, return nothing.
|
* @param {Object} [opt]
|
* @param {string} [opt.defaultMainType]
|
* @param {Array.<string>} [opt.includeMainTypes]
|
* @return {Object} result like:
|
* {
|
* seriesModels: [seriesModel1, seriesModel2],
|
* seriesModel: seriesModel1, // The first model
|
* geoModels: [geoModel1, geoModel2],
|
* geoModel: geoModel1, // The first model
|
* ...
|
* }
|
*/
|
modelUtil.parseFinder = function (ecModel, finder, opt) {
|
if (zrUtil.isString(finder)) {
|
var obj = {};
|
obj[finder + 'Index'] = 0;
|
finder = obj;
|
}
|
|
var defaultMainType = opt && opt.defaultMainType;
|
if (defaultMainType
|
&& !has(finder, defaultMainType + 'Index')
|
&& !has(finder, defaultMainType + 'Id')
|
&& !has(finder, defaultMainType + 'Name')
|
) {
|
finder[defaultMainType + 'Index'] = 0;
|
}
|
|
var result = {};
|
|
each(finder, function (value, key) {
|
var value = finder[key];
|
|
// Exclude 'dataIndex' and other illgal keys.
|
if (key === 'dataIndex' || key === 'dataIndexInside') {
|
result[key] = value;
|
return;
|
}
|
|
var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || [];
|
var mainType = parsedKey[1];
|
var queryType = (parsedKey[2] || '').toLowerCase();
|
|
if (!mainType
|
|| !queryType
|
|| value == null
|
|| (queryType === 'index' && value === 'none')
|
|| (opt && opt.includeMainTypes && zrUtil.indexOf(opt.includeMainTypes, mainType) < 0)
|
) {
|
return;
|
}
|
|
var queryParam = {mainType: mainType};
|
if (queryType !== 'index' || value !== 'all') {
|
queryParam[queryType] = value;
|
}
|
|
var models = ecModel.queryComponents(queryParam);
|
result[mainType + 'Models'] = models;
|
result[mainType + 'Model'] = models[0];
|
});
|
|
return result;
|
};
|
|
function has(obj, prop) {
|
return obj && obj.hasOwnProperty(prop);
|
}
|
|
module.exports = modelUtil;
|
|
|
|
/***/ },
|
/* 6 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
var textContain = __webpack_require__(8);
|
|
var formatUtil = {};
|
|
/**
|
* 每三位默认加,格式化
|
* @param {string|number} x
|
* @return {string}
|
*/
|
formatUtil.addCommas = function (x) {
|
if (isNaN(x)) {
|
return '-';
|
}
|
x = (x + '').split('.');
|
return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,')
|
+ (x.length > 1 ? ('.' + x[1]) : '');
|
};
|
|
/**
|
* @param {string} str
|
* @param {boolean} [upperCaseFirst=false]
|
* @return {string} str
|
*/
|
formatUtil.toCamelCase = function (str, upperCaseFirst) {
|
str = (str || '').toLowerCase().replace(/-(.)/g, function(match, group1) {
|
return group1.toUpperCase();
|
});
|
|
if (upperCaseFirst && str) {
|
str = str.charAt(0).toUpperCase() + str.slice(1);
|
}
|
|
return str;
|
};
|
|
/**
|
* Normalize css liked array configuration
|
* e.g.
|
* 3 => [3, 3, 3, 3]
|
* [4, 2] => [4, 2, 4, 2]
|
* [4, 3, 2] => [4, 3, 2, 3]
|
* @param {number|Array.<number>} val
|
*/
|
formatUtil.normalizeCssArray = function (val) {
|
var len = val.length;
|
if (typeof (val) === 'number') {
|
return [val, val, val, val];
|
}
|
else if (len === 2) {
|
// vertical | horizontal
|
return [val[0], val[1], val[0], val[1]];
|
}
|
else if (len === 3) {
|
// top | horizontal | bottom
|
return [val[0], val[1], val[2], val[1]];
|
}
|
return val;
|
};
|
|
var encodeHTML = formatUtil.encodeHTML = function (source) {
|
return String(source)
|
.replace(/&/g, '&')
|
.replace(/</g, '<')
|
.replace(/>/g, '>')
|
.replace(/"/g, '"')
|
.replace(/'/g, ''');
|
};
|
|
var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
|
|
var wrapVar = function (varName, seriesIdx) {
|
return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}';
|
};
|
|
/**
|
* Template formatter
|
* @param {string} tpl
|
* @param {Array.<Object>|Object} paramsList
|
* @param {boolean} [encode=false]
|
* @return {string}
|
*/
|
formatUtil.formatTpl = function (tpl, paramsList, encode) {
|
if (!zrUtil.isArray(paramsList)) {
|
paramsList = [paramsList];
|
}
|
var seriesLen = paramsList.length;
|
if (!seriesLen) {
|
return '';
|
}
|
|
var $vars = paramsList[0].$vars || [];
|
for (var i = 0; i < $vars.length; i++) {
|
var alias = TPL_VAR_ALIAS[i];
|
var val = wrapVar(alias, 0);
|
tpl = tpl.replace(wrapVar(alias), encode ? encodeHTML(val) : val);
|
}
|
for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) {
|
for (var k = 0; k < $vars.length; k++) {
|
var val = paramsList[seriesIdx][$vars[k]];
|
tpl = tpl.replace(
|
wrapVar(TPL_VAR_ALIAS[k], seriesIdx),
|
encode ? encodeHTML(val) : val
|
);
|
}
|
}
|
|
return tpl;
|
};
|
|
/**
|
* simple Template formatter
|
*
|
* @param {string} tpl
|
* @param {Object} param
|
* @param {boolean} [encode=false]
|
* @return {string}
|
*/
|
formatUtil.formatTplSimple = function (tpl, param, encode) {
|
zrUtil.each(param, function (value, key) {
|
tpl = tpl.replace(
|
'{' + key + '}',
|
encode ? encodeHTML(value) : value
|
);
|
});
|
return tpl;
|
};
|
|
|
/**
|
* @param {string} str
|
* @return {string}
|
* @inner
|
*/
|
var s2d = function (str) {
|
return str < 10 ? ('0' + str) : str;
|
};
|
|
/**
|
* ISO Date format
|
* @param {string} tpl
|
* @param {number} value
|
* @param {boolean} [isUTC=false] Default in local time.
|
* see `module:echarts/scale/Time`
|
* and `module:echarts/util/number#parseDate`.
|
* @inner
|
*/
|
formatUtil.formatTime = function (tpl, value, isUTC) {
|
if (tpl === 'week'
|
|| tpl === 'month'
|
|| tpl === 'quarter'
|
|| tpl === 'half-year'
|
|| tpl === 'year'
|
) {
|
tpl = 'MM-dd\nyyyy';
|
}
|
|
var date = numberUtil.parseDate(value);
|
var utc = isUTC ? 'UTC' : '';
|
var y = date['get' + utc + 'FullYear']();
|
var M = date['get' + utc + 'Month']() + 1;
|
var d = date['get' + utc + 'Date']();
|
var h = date['get' + utc + 'Hours']();
|
var m = date['get' + utc + 'Minutes']();
|
var s = date['get' + utc + 'Seconds']();
|
|
tpl = tpl.replace('MM', s2d(M))
|
.toLowerCase()
|
.replace('yyyy', y)
|
.replace('yy', y % 100)
|
.replace('dd', s2d(d))
|
.replace('d', d)
|
.replace('hh', s2d(h))
|
.replace('h', h)
|
.replace('mm', s2d(m))
|
.replace('m', m)
|
.replace('ss', s2d(s))
|
.replace('s', s);
|
|
return tpl;
|
};
|
|
/**
|
* Capital first
|
* @param {string} str
|
* @return {string}
|
*/
|
formatUtil.capitalFirst = function (str) {
|
return str ? str.charAt(0).toUpperCase() + str.substr(1) : str;
|
};
|
|
formatUtil.truncateText = textContain.truncateText;
|
|
module.exports = formatUtil;
|
|
|
|
/***/ },
|
/* 7 */
|
/***/ function(module, exports) {
|
|
/**
|
* 数值处理模块
|
* @module echarts/util/number
|
*/
|
|
|
|
var number = {};
|
|
var RADIAN_EPSILON = 1e-4;
|
|
function _trim(str) {
|
return str.replace(/^\s+/, '').replace(/\s+$/, '');
|
}
|
|
/**
|
* Linear mapping a value from domain to range
|
* @memberOf module:echarts/util/number
|
* @param {(number|Array.<number>)} val
|
* @param {Array.<number>} domain Domain extent domain[0] can be bigger than domain[1]
|
* @param {Array.<number>} range Range extent range[0] can be bigger than range[1]
|
* @param {boolean} clamp
|
* @return {(number|Array.<number>}
|
*/
|
number.linearMap = function (val, domain, range, clamp) {
|
var subDomain = domain[1] - domain[0];
|
var subRange = range[1] - range[0];
|
|
if (subDomain === 0) {
|
return subRange === 0
|
? range[0]
|
: (range[0] + range[1]) / 2;
|
}
|
|
// Avoid accuracy problem in edge, such as
|
// 146.39 - 62.83 === 83.55999999999999.
|
// See echarts/test/ut/spec/util/number.js#linearMap#accuracyError
|
// It is a little verbose for efficiency considering this method
|
// is a hotspot.
|
if (clamp) {
|
if (subDomain > 0) {
|
if (val <= domain[0]) {
|
return range[0];
|
}
|
else if (val >= domain[1]) {
|
return range[1];
|
}
|
}
|
else {
|
if (val >= domain[0]) {
|
return range[0];
|
}
|
else if (val <= domain[1]) {
|
return range[1];
|
}
|
}
|
}
|
else {
|
if (val === domain[0]) {
|
return range[0];
|
}
|
if (val === domain[1]) {
|
return range[1];
|
}
|
}
|
|
return (val - domain[0]) / subDomain * subRange + range[0];
|
};
|
|
/**
|
* Convert a percent string to absolute number.
|
* Returns NaN if percent is not a valid string or number
|
* @memberOf module:echarts/util/number
|
* @param {string|number} percent
|
* @param {number} all
|
* @return {number}
|
*/
|
number.parsePercent = function(percent, all) {
|
switch (percent) {
|
case 'center':
|
case 'middle':
|
percent = '50%';
|
break;
|
case 'left':
|
case 'top':
|
percent = '0%';
|
break;
|
case 'right':
|
case 'bottom':
|
percent = '100%';
|
break;
|
}
|
if (typeof percent === 'string') {
|
if (_trim(percent).match(/%$/)) {
|
return parseFloat(percent) / 100 * all;
|
}
|
|
return parseFloat(percent);
|
}
|
|
return percent == null ? NaN : +percent;
|
};
|
|
/**
|
* (1) Fix rounding error of float numbers.
|
* (2) Support return string to avoid scientific notation like '3.5e-7'.
|
*
|
* @param {number} x
|
* @param {number} [precision]
|
* @param {boolean} [returnStr]
|
* @return {number|string}
|
*/
|
number.round = function (x, precision, returnStr) {
|
if (precision == null) {
|
precision = 10;
|
}
|
// Avoid range error
|
precision = Math.min(Math.max(0, precision), 20);
|
x = (+x).toFixed(precision);
|
return returnStr ? x : +x;
|
};
|
|
number.asc = function (arr) {
|
arr.sort(function (a, b) {
|
return a - b;
|
});
|
return arr;
|
};
|
|
/**
|
* Get precision
|
* @param {number} val
|
*/
|
number.getPrecision = function (val) {
|
val = +val;
|
if (isNaN(val)) {
|
return 0;
|
}
|
// It is much faster than methods converting number to string as follows
|
// var tmp = val.toString();
|
// return tmp.length - 1 - tmp.indexOf('.');
|
// especially when precision is low
|
var e = 1;
|
var count = 0;
|
while (Math.round(val * e) / e !== val) {
|
e *= 10;
|
count++;
|
}
|
return count;
|
};
|
|
/**
|
* @param {string|number} val
|
* @return {number}
|
*/
|
number.getPrecisionSafe = function (val) {
|
var str = val.toString();
|
|
// Consider scientific notation: '3.4e-12' '3.4e+12'
|
var eIndex = str.indexOf('e');
|
if (eIndex > 0) {
|
var precision = +str.slice(eIndex + 1);
|
return precision < 0 ? -precision : 0;
|
}
|
else {
|
var dotIndex = str.indexOf('.');
|
return dotIndex < 0 ? 0 : str.length - 1 - dotIndex;
|
}
|
};
|
|
/**
|
* Minimal dicernible data precisioin according to a single pixel.
|
*
|
* @param {Array.<number>} dataExtent
|
* @param {Array.<number>} pixelExtent
|
* @return {number} precision
|
*/
|
number.getPixelPrecision = function (dataExtent, pixelExtent) {
|
var log = Math.log;
|
var LN10 = Math.LN10;
|
var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10);
|
var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10);
|
// toFixed() digits argument must be between 0 and 20.
|
var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20);
|
return !isFinite(precision) ? 20 : precision;
|
};
|
|
// Number.MAX_SAFE_INTEGER, ie do not support.
|
number.MAX_SAFE_INTEGER = 9007199254740991;
|
|
/**
|
* To 0 - 2 * PI, considering negative radian.
|
* @param {number} radian
|
* @return {number}
|
*/
|
number.remRadian = function (radian) {
|
var pi2 = Math.PI * 2;
|
return (radian % pi2 + pi2) % pi2;
|
};
|
|
/**
|
* @param {type} radian
|
* @return {boolean}
|
*/
|
number.isRadianAroundZero = function (val) {
|
return val > -RADIAN_EPSILON && val < RADIAN_EPSILON;
|
};
|
|
var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line
|
|
/**
|
* @return {number} in minutes
|
*/
|
number.getTimezoneOffset = function () {
|
return (new Date()).getTimezoneOffset();
|
};
|
|
/**
|
* @param {string|Date|number} value These values can be accepted:
|
* + An instance of Date, represent a time in its own time zone.
|
* + Or string in a subset of ISO 8601, only including:
|
* + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06',
|
* + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123',
|
* + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00',
|
* all of which will be treated as local time if time zone is not specified
|
* (see <https://momentjs.com/>).
|
* + Or other string format, including (all of which will be treated as loacal time):
|
* '2012', '2012-3-1', '2012/3/1', '2012/03/01',
|
* '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'
|
* + a timestamp, which represent a time in UTC.
|
* @return {Date} date
|
*/
|
number.parseDate = function (value) {
|
if (value instanceof Date) {
|
return value;
|
}
|
else if (typeof value === 'string') {
|
// Different browsers parse date in different way, so we parse it manually.
|
// Some other issues:
|
// new Date('1970-01-01') is UTC,
|
// new Date('1970/01/01') and new Date('1970-1-01') is local.
|
// See issue #3623
|
var match = TIME_REG.exec(value);
|
|
if (!match) {
|
// return Invalid Date.
|
return new Date(NaN);
|
}
|
|
var timezoneOffset = number.getTimezoneOffset();
|
var timeOffset = !match[8]
|
? 0
|
: match[8].toUpperCase() === 'Z'
|
? timezoneOffset
|
: +match[8].slice(0, 3) * 60 + timezoneOffset;
|
|
// match[n] can only be string or undefined.
|
// But take care of '12' + 1 => '121'.
|
return new Date(
|
+match[1],
|
+(match[2] || 1) - 1,
|
+match[3] || 1,
|
+match[4] || 0,
|
+(match[5] || 0) - timeOffset,
|
+match[6] || 0,
|
+match[7] || 0
|
);
|
}
|
else if (value == null) {
|
return new Date(NaN);
|
}
|
|
return new Date(Math.round(value));
|
};
|
|
/**
|
* Quantity of a number. e.g. 0.1, 1, 10, 100
|
*
|
* @param {number} val
|
* @return {number}
|
*/
|
number.quantity = function (val) {
|
return Math.pow(10, quantityExponent(val));
|
};
|
|
function quantityExponent(val) {
|
return Math.floor(Math.log(val) / Math.LN10);
|
}
|
|
/**
|
* find a “nice” number approximately equal to x. Round the number if round = true,
|
* take ceiling if round = false. The primary observation is that the “nicest”
|
* numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.
|
*
|
* See "Nice Numbers for Graph Labels" of Graphic Gems.
|
*
|
* @param {number} val Non-negative value.
|
* @param {boolean} round
|
* @return {number}
|
*/
|
number.nice = function (val, round) {
|
var exponent = quantityExponent(val);
|
var exp10 = Math.pow(10, exponent);
|
var f = val / exp10; // 1 <= f < 10
|
var nf;
|
if (round) {
|
if (f < 1.5) { nf = 1; }
|
else if (f < 2.5) { nf = 2; }
|
else if (f < 4) { nf = 3; }
|
else if (f < 7) { nf = 5; }
|
else { nf = 10; }
|
}
|
else {
|
if (f < 1) { nf = 1; }
|
else if (f < 2) { nf = 2; }
|
else if (f < 3) { nf = 3; }
|
else if (f < 5) { nf = 5; }
|
else { nf = 10; }
|
}
|
val = nf * exp10;
|
|
// Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).
|
// 20 is the uppper bound of toFixed.
|
return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val;
|
};
|
|
/**
|
* Order intervals asc, and split them when overlap.
|
* expect(numberUtil.reformIntervals([
|
* {interval: [18, 62], close: [1, 1]},
|
* {interval: [-Infinity, -70], close: [0, 0]},
|
* {interval: [-70, -26], close: [1, 1]},
|
* {interval: [-26, 18], close: [1, 1]},
|
* {interval: [62, 150], close: [1, 1]},
|
* {interval: [106, 150], close: [1, 1]},
|
* {interval: [150, Infinity], close: [0, 0]}
|
* ])).toEqual([
|
* {interval: [-Infinity, -70], close: [0, 0]},
|
* {interval: [-70, -26], close: [1, 1]},
|
* {interval: [-26, 18], close: [0, 1]},
|
* {interval: [18, 62], close: [0, 1]},
|
* {interval: [62, 150], close: [0, 1]},
|
* {interval: [150, Infinity], close: [0, 0]}
|
* ]);
|
* @param {Array.<Object>} list, where `close` mean open or close
|
* of the interval, and Infinity can be used.
|
* @return {Array.<Object>} The origin list, which has been reformed.
|
*/
|
number.reformIntervals = function (list) {
|
list.sort(function (a, b) {
|
return littleThan(a, b, 0) ? -1 : 1;
|
});
|
|
var curr = -Infinity;
|
var currClose = 1;
|
for (var i = 0; i < list.length;) {
|
var interval = list[i].interval;
|
var close = list[i].close;
|
|
for (var lg = 0; lg < 2; lg++) {
|
if (interval[lg] <= curr) {
|
interval[lg] = curr;
|
close[lg] = !lg ? 1 - currClose : 1;
|
}
|
curr = interval[lg];
|
currClose = close[lg];
|
}
|
|
if (interval[0] === interval[1] && close[0] * close[1] !== 1) {
|
list.splice(i, 1);
|
}
|
else {
|
i++;
|
}
|
}
|
|
return list;
|
|
function littleThan(a, b, lg) {
|
return a.interval[lg] < b.interval[lg]
|
|| (
|
a.interval[lg] === b.interval[lg]
|
&& (
|
(a.close[lg] - b.close[lg] === (!lg ? 1 : -1))
|
|| (!lg && littleThan(a, b, 1))
|
)
|
);
|
}
|
};
|
|
/**
|
* parseFloat NaNs numeric-cast false positives (null|true|false|"")
|
* ...but misinterprets leading-number strings, particularly hex literals ("0x...")
|
* subtraction forces infinities to NaN
|
*
|
* @param {*} v
|
* @return {boolean}
|
*/
|
number.isNumeric = function (v) {
|
return v - parseFloat(v) >= 0;
|
};
|
|
module.exports = number;
|
|
|
/***/ },
|
/* 8 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var textWidthCache = {};
|
var textWidthCacheCounter = 0;
|
var TEXT_CACHE_MAX = 5000;
|
|
var util = __webpack_require__(4);
|
var BoundingRect = __webpack_require__(9);
|
var retrieve = util.retrieve;
|
|
function getTextWidth(text, textFont) {
|
var key = text + ':' + textFont;
|
if (textWidthCache[key]) {
|
return textWidthCache[key];
|
}
|
|
var textLines = (text + '').split('\n');
|
var width = 0;
|
|
for (var i = 0, l = textLines.length; i < l; i++) {
|
// measureText 可以被覆盖以兼容不支持 Canvas 的环境
|
width = Math.max(textContain.measureText(textLines[i], textFont).width, width);
|
}
|
|
if (textWidthCacheCounter > TEXT_CACHE_MAX) {
|
textWidthCacheCounter = 0;
|
textWidthCache = {};
|
}
|
textWidthCacheCounter++;
|
textWidthCache[key] = width;
|
|
return width;
|
}
|
|
function getTextRect(text, textFont, textAlign, textBaseline) {
|
var textLineLen = ((text || '') + '').split('\n').length;
|
|
var width = getTextWidth(text, textFont);
|
// FIXME 高度计算比较粗暴
|
var lineHeight = getTextWidth('国', textFont);
|
var height = textLineLen * lineHeight;
|
|
var rect = new BoundingRect(0, 0, width, height);
|
// Text has a special line height property
|
rect.lineHeight = lineHeight;
|
|
switch (textBaseline) {
|
case 'bottom':
|
case 'alphabetic':
|
rect.y -= lineHeight;
|
break;
|
case 'middle':
|
rect.y -= lineHeight / 2;
|
break;
|
// case 'hanging':
|
// case 'top':
|
}
|
|
// FIXME Right to left language
|
switch (textAlign) {
|
case 'end':
|
case 'right':
|
rect.x -= rect.width;
|
break;
|
case 'center':
|
rect.x -= rect.width / 2;
|
break;
|
// case 'start':
|
// case 'left':
|
}
|
|
return rect;
|
}
|
|
function adjustTextPositionOnRect(textPosition, rect, textRect, distance) {
|
|
var x = rect.x;
|
var y = rect.y;
|
|
var height = rect.height;
|
var width = rect.width;
|
|
var textHeight = textRect.height;
|
|
var lineHeight = textRect.lineHeight;
|
var halfHeight = height / 2 - textHeight / 2 + lineHeight;
|
|
var textAlign = 'left';
|
|
switch (textPosition) {
|
case 'left':
|
x -= distance;
|
y += halfHeight;
|
textAlign = 'right';
|
break;
|
case 'right':
|
x += distance + width;
|
y += halfHeight;
|
textAlign = 'left';
|
break;
|
case 'top':
|
x += width / 2;
|
y -= distance + textHeight - lineHeight;
|
textAlign = 'center';
|
break;
|
case 'bottom':
|
x += width / 2;
|
y += height + distance + lineHeight;
|
textAlign = 'center';
|
break;
|
case 'inside':
|
x += width / 2;
|
y += halfHeight;
|
textAlign = 'center';
|
break;
|
case 'insideLeft':
|
x += distance;
|
y += halfHeight;
|
textAlign = 'left';
|
break;
|
case 'insideRight':
|
x += width - distance;
|
y += halfHeight;
|
textAlign = 'right';
|
break;
|
case 'insideTop':
|
x += width / 2;
|
y += distance + lineHeight;
|
textAlign = 'center';
|
break;
|
case 'insideBottom':
|
x += width / 2;
|
y += height - textHeight - distance + lineHeight;
|
textAlign = 'center';
|
break;
|
case 'insideTopLeft':
|
x += distance;
|
y += distance + lineHeight;
|
textAlign = 'left';
|
break;
|
case 'insideTopRight':
|
x += width - distance;
|
y += distance + lineHeight;
|
textAlign = 'right';
|
break;
|
case 'insideBottomLeft':
|
x += distance;
|
y += height - textHeight - distance + lineHeight;
|
break;
|
case 'insideBottomRight':
|
x += width - distance;
|
y += height - textHeight - distance + lineHeight;
|
textAlign = 'right';
|
break;
|
}
|
|
return {
|
x: x,
|
y: y,
|
textAlign: textAlign,
|
textBaseline: 'alphabetic'
|
};
|
}
|
|
/**
|
* Show ellipsis if overflow.
|
*
|
* @param {string} text
|
* @param {string} containerWidth
|
* @param {string} textFont
|
* @param {number} [ellipsis='...']
|
* @param {Object} [options]
|
* @param {number} [options.maxIterations=3]
|
* @param {number} [options.minChar=0] If truncate result are less
|
* then minChar, ellipsis will not show, which is
|
* better for user hint in some cases.
|
* @param {number} [options.placeholder=''] When all truncated, use the placeholder.
|
* @return {string}
|
*/
|
function truncateText(text, containerWidth, textFont, ellipsis, options) {
|
if (!containerWidth) {
|
return '';
|
}
|
|
options = options || {};
|
|
ellipsis = retrieve(ellipsis, '...');
|
var maxIterations = retrieve(options.maxIterations, 2);
|
var minChar = retrieve(options.minChar, 0);
|
// FIXME
|
// Other languages?
|
var cnCharWidth = getTextWidth('国', textFont);
|
// FIXME
|
// Consider proportional font?
|
var ascCharWidth = getTextWidth('a', textFont);
|
var placeholder = retrieve(options.placeholder, '');
|
|
// Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'.
|
// Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'.
|
var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap.
|
for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) {
|
contentWidth -= ascCharWidth;
|
}
|
|
var ellipsisWidth = getTextWidth(ellipsis);
|
if (ellipsisWidth > contentWidth) {
|
ellipsis = '';
|
ellipsisWidth = 0;
|
}
|
|
contentWidth = containerWidth - ellipsisWidth;
|
|
var textLines = (text + '').split('\n');
|
|
for (var i = 0, len = textLines.length; i < len; i++) {
|
var textLine = textLines[i];
|
var lineWidth = getTextWidth(textLine, textFont);
|
|
if (lineWidth <= containerWidth) {
|
continue;
|
}
|
|
for (var j = 0;; j++) {
|
if (lineWidth <= contentWidth || j >= maxIterations) {
|
textLine += ellipsis;
|
break;
|
}
|
|
var subLength = j === 0
|
? estimateLength(textLine, contentWidth, ascCharWidth, cnCharWidth)
|
: lineWidth > 0
|
? Math.floor(textLine.length * contentWidth / lineWidth)
|
: 0;
|
|
textLine = textLine.substr(0, subLength);
|
lineWidth = getTextWidth(textLine, textFont);
|
}
|
|
if (textLine === '') {
|
textLine = placeholder;
|
}
|
|
textLines[i] = textLine;
|
}
|
|
return textLines.join('\n');
|
}
|
|
function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) {
|
var width = 0;
|
var i = 0;
|
for (var len = text.length; i < len && width < contentWidth; i++) {
|
var charCode = text.charCodeAt(i);
|
width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth;
|
}
|
return i;
|
}
|
|
var textContain = {
|
|
getWidth: getTextWidth,
|
|
getBoundingRect: getTextRect,
|
|
adjustTextPositionOnRect: adjustTextPositionOnRect,
|
|
truncateText: truncateText,
|
|
measureText: function (text, textFont) {
|
var ctx = util.getContext();
|
ctx.font = textFont || '12px sans-serif';
|
return ctx.measureText(text);
|
}
|
};
|
|
module.exports = textContain;
|
|
|
/***/ },
|
/* 9 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* @module echarts/core/BoundingRect
|
*/
|
|
|
var vec2 = __webpack_require__(10);
|
var matrix = __webpack_require__(11);
|
|
var v2ApplyTransform = vec2.applyTransform;
|
var mathMin = Math.min;
|
var mathMax = Math.max;
|
/**
|
* @alias module:echarts/core/BoundingRect
|
*/
|
function BoundingRect(x, y, width, height) {
|
|
if (width < 0) {
|
x = x + width;
|
width = -width;
|
}
|
if (height < 0) {
|
y = y + height;
|
height = -height;
|
}
|
|
/**
|
* @type {number}
|
*/
|
this.x = x;
|
/**
|
* @type {number}
|
*/
|
this.y = y;
|
/**
|
* @type {number}
|
*/
|
this.width = width;
|
/**
|
* @type {number}
|
*/
|
this.height = height;
|
}
|
|
BoundingRect.prototype = {
|
|
constructor: BoundingRect,
|
|
/**
|
* @param {module:echarts/core/BoundingRect} other
|
*/
|
union: function (other) {
|
var x = mathMin(other.x, this.x);
|
var y = mathMin(other.y, this.y);
|
|
this.width = mathMax(
|
other.x + other.width,
|
this.x + this.width
|
) - x;
|
this.height = mathMax(
|
other.y + other.height,
|
this.y + this.height
|
) - y;
|
this.x = x;
|
this.y = y;
|
},
|
|
/**
|
* @param {Array.<number>} m
|
* @methods
|
*/
|
applyTransform: (function () {
|
var lt = [];
|
var rb = [];
|
var lb = [];
|
var rt = [];
|
return function (m) {
|
// In case usage like this
|
// el.getBoundingRect().applyTransform(el.transform)
|
// And element has no transform
|
if (!m) {
|
return;
|
}
|
lt[0] = lb[0] = this.x;
|
lt[1] = rt[1] = this.y;
|
rb[0] = rt[0] = this.x + this.width;
|
rb[1] = lb[1] = this.y + this.height;
|
|
v2ApplyTransform(lt, lt, m);
|
v2ApplyTransform(rb, rb, m);
|
v2ApplyTransform(lb, lb, m);
|
v2ApplyTransform(rt, rt, m);
|
|
this.x = mathMin(lt[0], rb[0], lb[0], rt[0]);
|
this.y = mathMin(lt[1], rb[1], lb[1], rt[1]);
|
var maxX = mathMax(lt[0], rb[0], lb[0], rt[0]);
|
var maxY = mathMax(lt[1], rb[1], lb[1], rt[1]);
|
this.width = maxX - this.x;
|
this.height = maxY - this.y;
|
};
|
})(),
|
|
/**
|
* Calculate matrix of transforming from self to target rect
|
* @param {module:zrender/core/BoundingRect} b
|
* @return {Array.<number>}
|
*/
|
calculateTransform: function (b) {
|
var a = this;
|
var sx = b.width / a.width;
|
var sy = b.height / a.height;
|
|
var m = matrix.create();
|
|
// 矩阵右乘
|
matrix.translate(m, m, [-a.x, -a.y]);
|
matrix.scale(m, m, [sx, sy]);
|
matrix.translate(m, m, [b.x, b.y]);
|
|
return m;
|
},
|
|
/**
|
* @param {(module:echarts/core/BoundingRect|Object)} b
|
* @return {boolean}
|
*/
|
intersect: function (b) {
|
if (!b) {
|
return false;
|
}
|
|
if (!(b instanceof BoundingRect)) {
|
// Normalize negative width/height.
|
b = BoundingRect.create(b);
|
}
|
|
var a = this;
|
var ax0 = a.x;
|
var ax1 = a.x + a.width;
|
var ay0 = a.y;
|
var ay1 = a.y + a.height;
|
|
var bx0 = b.x;
|
var bx1 = b.x + b.width;
|
var by0 = b.y;
|
var by1 = b.y + b.height;
|
|
return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);
|
},
|
|
contain: function (x, y) {
|
var rect = this;
|
return x >= rect.x
|
&& x <= (rect.x + rect.width)
|
&& y >= rect.y
|
&& y <= (rect.y + rect.height);
|
},
|
|
/**
|
* @return {module:echarts/core/BoundingRect}
|
*/
|
clone: function () {
|
return new BoundingRect(this.x, this.y, this.width, this.height);
|
},
|
|
/**
|
* Copy from another rect
|
*/
|
copy: function (other) {
|
this.x = other.x;
|
this.y = other.y;
|
this.width = other.width;
|
this.height = other.height;
|
},
|
|
plain: function () {
|
return {
|
x: this.x,
|
y: this.y,
|
width: this.width,
|
height: this.height
|
};
|
}
|
};
|
|
/**
|
* @param {Object|module:zrender/core/BoundingRect} rect
|
* @param {number} rect.x
|
* @param {number} rect.y
|
* @param {number} rect.width
|
* @param {number} rect.height
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
BoundingRect.create = function (rect) {
|
return new BoundingRect(rect.x, rect.y, rect.width, rect.height);
|
};
|
|
module.exports = BoundingRect;
|
|
|
/***/ },
|
/* 10 */
|
/***/ function(module, exports) {
|
|
|
var ArrayCtor = typeof Float32Array === 'undefined'
|
? Array
|
: Float32Array;
|
|
/**
|
* @typedef {Float32Array|Array.<number>} Vector2
|
*/
|
/**
|
* 二维向量类
|
* @exports zrender/tool/vector
|
*/
|
var vector = {
|
/**
|
* 创建一个向量
|
* @param {number} [x=0]
|
* @param {number} [y=0]
|
* @return {Vector2}
|
*/
|
create: function (x, y) {
|
var out = new ArrayCtor(2);
|
if (x == null) {
|
x = 0;
|
}
|
if (y == null) {
|
y = 0;
|
}
|
out[0] = x;
|
out[1] = y;
|
return out;
|
},
|
|
/**
|
* 复制向量数据
|
* @param {Vector2} out
|
* @param {Vector2} v
|
* @return {Vector2}
|
*/
|
copy: function (out, v) {
|
out[0] = v[0];
|
out[1] = v[1];
|
return out;
|
},
|
|
/**
|
* 克隆一个向量
|
* @param {Vector2} v
|
* @return {Vector2}
|
*/
|
clone: function (v) {
|
var out = new ArrayCtor(2);
|
out[0] = v[0];
|
out[1] = v[1];
|
return out;
|
},
|
|
/**
|
* 设置向量的两个项
|
* @param {Vector2} out
|
* @param {number} a
|
* @param {number} b
|
* @return {Vector2} 结果
|
*/
|
set: function (out, a, b) {
|
out[0] = a;
|
out[1] = b;
|
return out;
|
},
|
|
/**
|
* 向量相加
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
add: function (out, v1, v2) {
|
out[0] = v1[0] + v2[0];
|
out[1] = v1[1] + v2[1];
|
return out;
|
},
|
|
/**
|
* 向量缩放后相加
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
* @param {number} a
|
*/
|
scaleAndAdd: function (out, v1, v2, a) {
|
out[0] = v1[0] + v2[0] * a;
|
out[1] = v1[1] + v2[1] * a;
|
return out;
|
},
|
|
/**
|
* 向量相减
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
sub: function (out, v1, v2) {
|
out[0] = v1[0] - v2[0];
|
out[1] = v1[1] - v2[1];
|
return out;
|
},
|
|
/**
|
* 向量长度
|
* @param {Vector2} v
|
* @return {number}
|
*/
|
len: function (v) {
|
return Math.sqrt(this.lenSquare(v));
|
},
|
|
/**
|
* 向量长度平方
|
* @param {Vector2} v
|
* @return {number}
|
*/
|
lenSquare: function (v) {
|
return v[0] * v[0] + v[1] * v[1];
|
},
|
|
/**
|
* 向量乘法
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
mul: function (out, v1, v2) {
|
out[0] = v1[0] * v2[0];
|
out[1] = v1[1] * v2[1];
|
return out;
|
},
|
|
/**
|
* 向量除法
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
div: function (out, v1, v2) {
|
out[0] = v1[0] / v2[0];
|
out[1] = v1[1] / v2[1];
|
return out;
|
},
|
|
/**
|
* 向量点乘
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
* @return {number}
|
*/
|
dot: function (v1, v2) {
|
return v1[0] * v2[0] + v1[1] * v2[1];
|
},
|
|
/**
|
* 向量缩放
|
* @param {Vector2} out
|
* @param {Vector2} v
|
* @param {number} s
|
*/
|
scale: function (out, v, s) {
|
out[0] = v[0] * s;
|
out[1] = v[1] * s;
|
return out;
|
},
|
|
/**
|
* 向量归一化
|
* @param {Vector2} out
|
* @param {Vector2} v
|
*/
|
normalize: function (out, v) {
|
var d = vector.len(v);
|
if (d === 0) {
|
out[0] = 0;
|
out[1] = 0;
|
}
|
else {
|
out[0] = v[0] / d;
|
out[1] = v[1] / d;
|
}
|
return out;
|
},
|
|
/**
|
* 计算向量间距离
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
* @return {number}
|
*/
|
distance: function (v1, v2) {
|
return Math.sqrt(
|
(v1[0] - v2[0]) * (v1[0] - v2[0])
|
+ (v1[1] - v2[1]) * (v1[1] - v2[1])
|
);
|
},
|
|
/**
|
* 向量距离平方
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
* @return {number}
|
*/
|
distanceSquare: function (v1, v2) {
|
return (v1[0] - v2[0]) * (v1[0] - v2[0])
|
+ (v1[1] - v2[1]) * (v1[1] - v2[1]);
|
},
|
|
/**
|
* 求负向量
|
* @param {Vector2} out
|
* @param {Vector2} v
|
*/
|
negate: function (out, v) {
|
out[0] = -v[0];
|
out[1] = -v[1];
|
return out;
|
},
|
|
/**
|
* 插值两个点
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
* @param {number} t
|
*/
|
lerp: function (out, v1, v2, t) {
|
out[0] = v1[0] + t * (v2[0] - v1[0]);
|
out[1] = v1[1] + t * (v2[1] - v1[1]);
|
return out;
|
},
|
|
/**
|
* 矩阵左乘向量
|
* @param {Vector2} out
|
* @param {Vector2} v
|
* @param {Vector2} m
|
*/
|
applyTransform: function (out, v, m) {
|
var x = v[0];
|
var y = v[1];
|
out[0] = m[0] * x + m[2] * y + m[4];
|
out[1] = m[1] * x + m[3] * y + m[5];
|
return out;
|
},
|
/**
|
* 求两个向量最小值
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
min: function (out, v1, v2) {
|
out[0] = Math.min(v1[0], v2[0]);
|
out[1] = Math.min(v1[1], v2[1]);
|
return out;
|
},
|
/**
|
* 求两个向量最大值
|
* @param {Vector2} out
|
* @param {Vector2} v1
|
* @param {Vector2} v2
|
*/
|
max: function (out, v1, v2) {
|
out[0] = Math.max(v1[0], v2[0]);
|
out[1] = Math.max(v1[1], v2[1]);
|
return out;
|
}
|
};
|
|
vector.length = vector.len;
|
vector.lengthSquare = vector.lenSquare;
|
vector.dist = vector.distance;
|
vector.distSquare = vector.distanceSquare;
|
|
module.exports = vector;
|
|
|
|
/***/ },
|
/* 11 */
|
/***/ function(module, exports) {
|
|
|
var ArrayCtor = typeof Float32Array === 'undefined'
|
? Array
|
: Float32Array;
|
/**
|
* 3x2矩阵操作类
|
* @exports zrender/tool/matrix
|
*/
|
var matrix = {
|
/**
|
* 创建一个单位矩阵
|
* @return {Float32Array|Array.<number>}
|
*/
|
create : function() {
|
var out = new ArrayCtor(6);
|
matrix.identity(out);
|
|
return out;
|
},
|
/**
|
* 设置矩阵为单位矩阵
|
* @param {Float32Array|Array.<number>} out
|
*/
|
identity : function(out) {
|
out[0] = 1;
|
out[1] = 0;
|
out[2] = 0;
|
out[3] = 1;
|
out[4] = 0;
|
out[5] = 0;
|
return out;
|
},
|
/**
|
* 复制矩阵
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} m
|
*/
|
copy: function(out, m) {
|
out[0] = m[0];
|
out[1] = m[1];
|
out[2] = m[2];
|
out[3] = m[3];
|
out[4] = m[4];
|
out[5] = m[5];
|
return out;
|
},
|
/**
|
* 矩阵相乘
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} m1
|
* @param {Float32Array|Array.<number>} m2
|
*/
|
mul : function (out, m1, m2) {
|
// Consider matrix.mul(m, m2, m);
|
// where out is the same as m2.
|
// So use temp variable to escape error.
|
var out0 = m1[0] * m2[0] + m1[2] * m2[1];
|
var out1 = m1[1] * m2[0] + m1[3] * m2[1];
|
var out2 = m1[0] * m2[2] + m1[2] * m2[3];
|
var out3 = m1[1] * m2[2] + m1[3] * m2[3];
|
var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];
|
var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];
|
out[0] = out0;
|
out[1] = out1;
|
out[2] = out2;
|
out[3] = out3;
|
out[4] = out4;
|
out[5] = out5;
|
return out;
|
},
|
/**
|
* 平移变换
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} a
|
* @param {Float32Array|Array.<number>} v
|
*/
|
translate : function(out, a, v) {
|
out[0] = a[0];
|
out[1] = a[1];
|
out[2] = a[2];
|
out[3] = a[3];
|
out[4] = a[4] + v[0];
|
out[5] = a[5] + v[1];
|
return out;
|
},
|
/**
|
* 旋转变换
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} a
|
* @param {number} rad
|
*/
|
rotate : function(out, a, rad) {
|
var aa = a[0];
|
var ac = a[2];
|
var atx = a[4];
|
var ab = a[1];
|
var ad = a[3];
|
var aty = a[5];
|
var st = Math.sin(rad);
|
var ct = Math.cos(rad);
|
|
out[0] = aa * ct + ab * st;
|
out[1] = -aa * st + ab * ct;
|
out[2] = ac * ct + ad * st;
|
out[3] = -ac * st + ct * ad;
|
out[4] = ct * atx + st * aty;
|
out[5] = ct * aty - st * atx;
|
return out;
|
},
|
/**
|
* 缩放变换
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} a
|
* @param {Float32Array|Array.<number>} v
|
*/
|
scale : function(out, a, v) {
|
var vx = v[0];
|
var vy = v[1];
|
out[0] = a[0] * vx;
|
out[1] = a[1] * vy;
|
out[2] = a[2] * vx;
|
out[3] = a[3] * vy;
|
out[4] = a[4] * vx;
|
out[5] = a[5] * vy;
|
return out;
|
},
|
/**
|
* 求逆矩阵
|
* @param {Float32Array|Array.<number>} out
|
* @param {Float32Array|Array.<number>} a
|
*/
|
invert : function(out, a) {
|
|
var aa = a[0];
|
var ac = a[2];
|
var atx = a[4];
|
var ab = a[1];
|
var ad = a[3];
|
var aty = a[5];
|
|
var det = aa * ad - ab * ac;
|
if (!det) {
|
return null;
|
}
|
det = 1.0 / det;
|
|
out[0] = ad * det;
|
out[1] = -ab * det;
|
out[2] = -ac * det;
|
out[3] = aa * det;
|
out[4] = (ac * aty - ad * atx) * det;
|
out[5] = (ab * atx - aa * aty) * det;
|
return out;
|
}
|
};
|
|
module.exports = matrix;
|
|
|
|
/***/ },
|
/* 12 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/model/Model
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var clazzUtil = __webpack_require__(13);
|
var env = __webpack_require__(2);
|
|
/**
|
* @alias module:echarts/model/Model
|
* @constructor
|
* @param {Object} option
|
* @param {module:echarts/model/Model} [parentModel]
|
* @param {module:echarts/model/Global} [ecModel]
|
*/
|
function Model(option, parentModel, ecModel) {
|
/**
|
* @type {module:echarts/model/Model}
|
* @readOnly
|
*/
|
this.parentModel = parentModel;
|
|
/**
|
* @type {module:echarts/model/Global}
|
* @readOnly
|
*/
|
this.ecModel = ecModel;
|
|
/**
|
* @type {Object}
|
* @protected
|
*/
|
this.option = option;
|
|
// Simple optimization
|
// if (this.init) {
|
// if (arguments.length <= 4) {
|
// this.init(option, parentModel, ecModel, extraOpt);
|
// }
|
// else {
|
// this.init.apply(this, arguments);
|
// }
|
// }
|
}
|
|
Model.prototype = {
|
|
constructor: Model,
|
|
/**
|
* Model 的初始化函数
|
* @param {Object} option
|
*/
|
init: null,
|
|
/**
|
* 从新的 Option merge
|
*/
|
mergeOption: function (option) {
|
zrUtil.merge(this.option, option, true);
|
},
|
|
/**
|
* @param {string|Array.<string>} path
|
* @param {boolean} [ignoreParent=false]
|
* @return {*}
|
*/
|
get: function (path, ignoreParent) {
|
if (path == null) {
|
return this.option;
|
}
|
|
return doGet(
|
this.option,
|
this.parsePath(path),
|
!ignoreParent && getParent(this, path)
|
);
|
},
|
|
/**
|
* @param {string} key
|
* @param {boolean} [ignoreParent=false]
|
* @return {*}
|
*/
|
getShallow: function (key, ignoreParent) {
|
var option = this.option;
|
|
var val = option == null ? option : option[key];
|
var parentModel = !ignoreParent && getParent(this, key);
|
if (val == null && parentModel) {
|
val = parentModel.getShallow(key);
|
}
|
return val;
|
},
|
|
/**
|
* @param {string|Array.<string>} path
|
* @param {module:echarts/model/Model} [parentModel]
|
* @return {module:echarts/model/Model}
|
*/
|
getModel: function (path, parentModel) {
|
var obj = path == null
|
? this.option
|
: doGet(this.option, path = this.parsePath(path));
|
|
var thisParentModel;
|
parentModel = parentModel || (
|
(thisParentModel = getParent(this, path))
|
&& thisParentModel.getModel(path)
|
);
|
|
return new Model(obj, parentModel, this.ecModel);
|
},
|
|
/**
|
* If model has option
|
*/
|
isEmpty: function () {
|
return this.option == null;
|
},
|
|
restoreData: function () {},
|
|
// Pending
|
clone: function () {
|
var Ctor = this.constructor;
|
return new Ctor(zrUtil.clone(this.option));
|
},
|
|
setReadOnly: function (properties) {
|
clazzUtil.setReadOnly(this, properties);
|
},
|
|
// If path is null/undefined, return null/undefined.
|
parsePath: function(path) {
|
if (typeof path === 'string') {
|
path = path.split('.');
|
}
|
return path;
|
},
|
|
/**
|
* @param {Function} getParentMethod
|
* param {Array.<string>|string} path
|
* return {module:echarts/model/Model}
|
*/
|
customizeGetParent: function (getParentMethod) {
|
clazzUtil.set(this, 'getParent', getParentMethod);
|
},
|
|
isAnimationEnabled: function () {
|
if (!env.node) {
|
if (this.option.animation != null) {
|
return !!this.option.animation;
|
}
|
else if (this.parentModel) {
|
return this.parentModel.isAnimationEnabled();
|
}
|
}
|
}
|
};
|
|
function doGet(obj, pathArr, parentModel) {
|
for (var i = 0; i < pathArr.length; i++) {
|
// Ignore empty
|
if (!pathArr[i]) {
|
continue;
|
}
|
// obj could be number/string/... (like 0)
|
obj = (obj && typeof obj === 'object') ? obj[pathArr[i]] : null;
|
if (obj == null) {
|
break;
|
}
|
}
|
if (obj == null && parentModel) {
|
obj = parentModel.get(pathArr);
|
}
|
return obj;
|
}
|
|
function getParent(model, path) {
|
var getParentMethod = clazzUtil.get(model, 'getParent');
|
return getParentMethod ? getParentMethod.call(model, path) : model.parentModel;
|
}
|
|
// Enable Model.extend.
|
clazzUtil.enableClassExtend(Model);
|
|
var mixin = zrUtil.mixin;
|
mixin(Model, __webpack_require__(14));
|
mixin(Model, __webpack_require__(16));
|
mixin(Model, __webpack_require__(17));
|
mixin(Model, __webpack_require__(18));
|
|
module.exports = Model;
|
|
|
/***/ },
|
/* 13 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var clazz = {};
|
|
var TYPE_DELIMITER = '.';
|
var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
|
var MEMBER_PRIFIX = '\0ec_\0';
|
|
/**
|
* Hide private class member.
|
* The same behavior as `host[name] = value;` (can be right-value)
|
* @public
|
*/
|
clazz.set = function (host, name, value) {
|
return (host[MEMBER_PRIFIX + name] = value);
|
};
|
|
/**
|
* Hide private class member.
|
* The same behavior as `host[name];`
|
* @public
|
*/
|
clazz.get = function (host, name) {
|
return host[MEMBER_PRIFIX + name];
|
};
|
|
/**
|
* For hidden private class member.
|
* The same behavior as `host.hasOwnProperty(name);`
|
* @public
|
*/
|
clazz.hasOwn = function (host, name) {
|
return host.hasOwnProperty(MEMBER_PRIFIX + name);
|
};
|
|
/**
|
* Notice, parseClassType('') should returns {main: '', sub: ''}
|
* @public
|
*/
|
var parseClassType = clazz.parseClassType = function (componentType) {
|
var ret = {main: '', sub: ''};
|
if (componentType) {
|
componentType = componentType.split(TYPE_DELIMITER);
|
ret.main = componentType[0] || '';
|
ret.sub = componentType[1] || '';
|
}
|
return ret;
|
};
|
|
/**
|
* @public
|
*/
|
function checkClassType(componentType) {
|
zrUtil.assert(
|
/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),
|
'componentType "' + componentType + '" illegal'
|
);
|
}
|
|
/**
|
* @public
|
*/
|
clazz.enableClassExtend = function (RootClass, mandatoryMethods) {
|
|
RootClass.$constructor = RootClass;
|
RootClass.extend = function (proto) {
|
|
if (true) {
|
zrUtil.each(mandatoryMethods, function (method) {
|
if (!proto[method]) {
|
console.warn(
|
'Method `' + method + '` should be implemented'
|
+ (proto.type ? ' in ' + proto.type : '') + '.'
|
);
|
}
|
});
|
}
|
|
var superClass = this;
|
var ExtendedClass = function () {
|
if (!proto.$constructor) {
|
superClass.apply(this, arguments);
|
}
|
else {
|
proto.$constructor.apply(this, arguments);
|
}
|
};
|
|
zrUtil.extend(ExtendedClass.prototype, proto);
|
|
ExtendedClass.extend = this.extend;
|
ExtendedClass.superCall = superCall;
|
ExtendedClass.superApply = superApply;
|
zrUtil.inherits(ExtendedClass, this);
|
ExtendedClass.superClass = superClass;
|
|
return ExtendedClass;
|
};
|
};
|
|
// superCall should have class info, which can not be fetch from 'this'.
|
// Consider this case:
|
// class A has method f,
|
// class B inherits class A, overrides method f, f call superApply('f'),
|
// class C inherits class B, do not overrides method f,
|
// then when method of class C is called, dead loop occured.
|
function superCall(context, methodName) {
|
var args = zrUtil.slice(arguments, 2);
|
return this.superClass.prototype[methodName].apply(context, args);
|
}
|
|
function superApply(context, methodName, args) {
|
return this.superClass.prototype[methodName].apply(context, args);
|
}
|
|
/**
|
* @param {Object} entity
|
* @param {Object} options
|
* @param {boolean} [options.registerWhenExtend]
|
* @public
|
*/
|
clazz.enableClassManagement = function (entity, options) {
|
options = options || {};
|
|
/**
|
* Component model classes
|
* key: componentType,
|
* value:
|
* componentClass, when componentType is 'xxx'
|
* or Object.<subKey, componentClass>, when componentType is 'xxx.yy'
|
* @type {Object}
|
*/
|
var storage = {};
|
|
entity.registerClass = function (Clazz, componentType) {
|
if (componentType) {
|
checkClassType(componentType);
|
componentType = parseClassType(componentType);
|
|
if (!componentType.sub) {
|
if (true) {
|
if (storage[componentType.main]) {
|
console.warn(componentType.main + ' exists.');
|
}
|
}
|
storage[componentType.main] = Clazz;
|
}
|
else if (componentType.sub !== IS_CONTAINER) {
|
var container = makeContainer(componentType);
|
container[componentType.sub] = Clazz;
|
}
|
}
|
return Clazz;
|
};
|
|
entity.getClass = function (componentMainType, subType, throwWhenNotFound) {
|
var Clazz = storage[componentMainType];
|
|
if (Clazz && Clazz[IS_CONTAINER]) {
|
Clazz = subType ? Clazz[subType] : null;
|
}
|
|
if (throwWhenNotFound && !Clazz) {
|
throw new Error(
|
!subType
|
? componentMainType + '.' + 'type should be specified.'
|
: 'Component ' + componentMainType + '.' + (subType || '') + ' not exists. Load it first.'
|
);
|
}
|
|
return Clazz;
|
};
|
|
entity.getClassesByMainType = function (componentType) {
|
componentType = parseClassType(componentType);
|
|
var result = [];
|
var obj = storage[componentType.main];
|
|
if (obj && obj[IS_CONTAINER]) {
|
zrUtil.each(obj, function (o, type) {
|
type !== IS_CONTAINER && result.push(o);
|
});
|
}
|
else {
|
result.push(obj);
|
}
|
|
return result;
|
};
|
|
entity.hasClass = function (componentType) {
|
// Just consider componentType.main.
|
componentType = parseClassType(componentType);
|
return !!storage[componentType.main];
|
};
|
|
/**
|
* @return {Array.<string>} Like ['aa', 'bb'], but can not be ['aa.xx']
|
*/
|
entity.getAllClassMainTypes = function () {
|
var types = [];
|
zrUtil.each(storage, function (obj, type) {
|
types.push(type);
|
});
|
return types;
|
};
|
|
/**
|
* If a main type is container and has sub types
|
* @param {string} mainType
|
* @return {boolean}
|
*/
|
entity.hasSubTypes = function (componentType) {
|
componentType = parseClassType(componentType);
|
var obj = storage[componentType.main];
|
return obj && obj[IS_CONTAINER];
|
};
|
|
entity.parseClassType = parseClassType;
|
|
function makeContainer(componentType) {
|
var container = storage[componentType.main];
|
if (!container || !container[IS_CONTAINER]) {
|
container = storage[componentType.main] = {};
|
container[IS_CONTAINER] = true;
|
}
|
return container;
|
}
|
|
if (options.registerWhenExtend) {
|
var originalExtend = entity.extend;
|
if (originalExtend) {
|
entity.extend = function (proto) {
|
var ExtendedClass = originalExtend.call(this, proto);
|
return entity.registerClass(ExtendedClass, proto.type);
|
};
|
}
|
}
|
|
return entity;
|
};
|
|
/**
|
* @param {string|Array.<string>} properties
|
*/
|
clazz.setReadOnly = function (obj, properties) {
|
// FIXME It seems broken in IE8 simulation of IE11
|
// if (!zrUtil.isArray(properties)) {
|
// properties = properties != null ? [properties] : [];
|
// }
|
// zrUtil.each(properties, function (prop) {
|
// var value = obj[prop];
|
|
// Object.defineProperty
|
// && Object.defineProperty(obj, prop, {
|
// value: value, writable: false
|
// });
|
// zrUtil.isArray(obj[prop])
|
// && Object.freeze
|
// && Object.freeze(obj[prop]);
|
// });
|
};
|
|
module.exports = clazz;
|
|
|
/***/ },
|
/* 14 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var getLineStyle = __webpack_require__(15)(
|
[
|
['lineWidth', 'width'],
|
['stroke', 'color'],
|
['opacity'],
|
['shadowBlur'],
|
['shadowOffsetX'],
|
['shadowOffsetY'],
|
['shadowColor']
|
]
|
);
|
module.exports = {
|
getLineStyle: function (excludes) {
|
var style = getLineStyle.call(this, excludes);
|
var lineDash = this.getLineDash(style.lineWidth);
|
lineDash && (style.lineDash = lineDash);
|
return style;
|
},
|
|
getLineDash: function (lineWidth) {
|
if (lineWidth == null) {
|
lineWidth = 1;
|
}
|
var lineType = this.get('type');
|
var dotSize = Math.max(lineWidth, 2);
|
var dashSize = lineWidth * 4;
|
return (lineType === 'solid' || lineType == null) ? null
|
: (lineType === 'dashed' ? [dashSize, dashSize] : [dotSize, dotSize]);
|
}
|
};
|
|
|
/***/ },
|
/* 15 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// TODO Parse shadow style
|
// TODO Only shallow path support
|
|
var zrUtil = __webpack_require__(4);
|
|
module.exports = function (properties) {
|
// Normalize
|
for (var i = 0; i < properties.length; i++) {
|
if (!properties[i][1]) {
|
properties[i][1] = properties[i][0];
|
}
|
}
|
return function (excludes, includes) {
|
var style = {};
|
for (var i = 0; i < properties.length; i++) {
|
var propName = properties[i][1];
|
if ((excludes && zrUtil.indexOf(excludes, propName) >= 0)
|
|| (includes && zrUtil.indexOf(includes, propName) < 0)
|
) {
|
continue;
|
}
|
var val = this.getShallow(propName);
|
if (val != null) {
|
style[properties[i][0]] = val;
|
}
|
}
|
return style;
|
};
|
};
|
|
|
/***/ },
|
/* 16 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
module.exports = {
|
getAreaStyle: __webpack_require__(15)(
|
[
|
['fill', 'color'],
|
['shadowBlur'],
|
['shadowOffsetX'],
|
['shadowOffsetY'],
|
['opacity'],
|
['shadowColor']
|
]
|
)
|
};
|
|
|
/***/ },
|
/* 17 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var textContain = __webpack_require__(8);
|
|
function getShallow(model, path) {
|
return model && model.getShallow(path);
|
}
|
|
module.exports = {
|
/**
|
* Get color property or get color from option.textStyle.color
|
* @return {string}
|
*/
|
getTextColor: function () {
|
var ecModel = this.ecModel;
|
return this.getShallow('color')
|
|| (ecModel && ecModel.get('textStyle.color'));
|
},
|
|
/**
|
* Create font string from fontStyle, fontWeight, fontSize, fontFamily
|
* @return {string}
|
*/
|
getFont: function () {
|
var ecModel = this.ecModel;
|
var gTextStyleModel = ecModel && ecModel.getModel('textStyle');
|
return [
|
// FIXME in node-canvas fontWeight is before fontStyle
|
this.getShallow('fontStyle') || getShallow(gTextStyleModel, 'fontStyle'),
|
this.getShallow('fontWeight') || getShallow(gTextStyleModel, 'fontWeight'),
|
(this.getShallow('fontSize') || getShallow(gTextStyleModel, 'fontSize') || 12) + 'px',
|
this.getShallow('fontFamily') || getShallow(gTextStyleModel, 'fontFamily') || 'sans-serif'
|
].join(' ');
|
},
|
|
getTextRect: function (text) {
|
return textContain.getBoundingRect(
|
text,
|
this.getFont(),
|
this.getShallow('align'),
|
this.getShallow('baseline')
|
);
|
},
|
|
truncateText: function (text, containerWidth, ellipsis, options) {
|
return textContain.truncateText(
|
text, containerWidth, this.getFont(), ellipsis, options
|
);
|
}
|
};
|
|
|
/***/ },
|
/* 18 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var getItemStyle = __webpack_require__(15)(
|
[
|
['fill', 'color'],
|
['stroke', 'borderColor'],
|
['lineWidth', 'borderWidth'],
|
['opacity'],
|
['shadowBlur'],
|
['shadowOffsetX'],
|
['shadowOffsetY'],
|
['shadowColor'],
|
['textPosition'],
|
['textAlign']
|
]
|
);
|
module.exports = {
|
getItemStyle: function (excludes, includes) {
|
var style = getItemStyle.call(this, excludes, includes);
|
var lineDash = this.getBorderLineDash();
|
lineDash && (style.lineDash = lineDash);
|
return style;
|
},
|
|
getBorderLineDash: function () {
|
var lineType = this.get('borderType');
|
return (lineType === 'solid' || lineType == null) ? null
|
: (lineType === 'dashed' ? [5, 5] : [1, 1]);
|
}
|
};
|
|
|
/***/ },
|
/* 19 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Component model
|
*
|
* @module echarts/model/Component
|
*/
|
|
|
var Model = __webpack_require__(12);
|
var zrUtil = __webpack_require__(4);
|
var arrayPush = Array.prototype.push;
|
var componentUtil = __webpack_require__(20);
|
var clazzUtil = __webpack_require__(13);
|
var layout = __webpack_require__(21);
|
|
/**
|
* @alias module:echarts/model/Component
|
* @constructor
|
* @param {Object} option
|
* @param {module:echarts/model/Model} parentModel
|
* @param {module:echarts/model/Model} ecModel
|
*/
|
var ComponentModel = Model.extend({
|
|
type: 'component',
|
|
/**
|
* @readOnly
|
* @type {string}
|
*/
|
id: '',
|
|
/**
|
* @readOnly
|
*/
|
name: '',
|
|
/**
|
* @readOnly
|
* @type {string}
|
*/
|
mainType: '',
|
|
/**
|
* @readOnly
|
* @type {string}
|
*/
|
subType: '',
|
|
/**
|
* @readOnly
|
* @type {number}
|
*/
|
componentIndex: 0,
|
|
/**
|
* @type {Object}
|
* @protected
|
*/
|
defaultOption: null,
|
|
/**
|
* @type {module:echarts/model/Global}
|
* @readOnly
|
*/
|
ecModel: null,
|
|
/**
|
* key: componentType
|
* value: Component model list, can not be null.
|
* @type {Object.<string, Array.<module:echarts/model/Model>>}
|
* @readOnly
|
*/
|
dependentModels: [],
|
|
/**
|
* @type {string}
|
* @readOnly
|
*/
|
uid: null,
|
|
/**
|
* Support merge layout params.
|
* Only support 'box' now (left/right/top/bottom/width/height).
|
* @type {string|Object} Object can be {ignoreSize: true}
|
* @readOnly
|
*/
|
layoutMode: null,
|
|
$constructor: function (option, parentModel, ecModel, extraOpt) {
|
Model.call(this, option, parentModel, ecModel, extraOpt);
|
|
this.uid = componentUtil.getUID('componentModel');
|
},
|
|
|
init: function (option, parentModel, ecModel, extraOpt) {
|
this.mergeDefaultAndTheme(option, ecModel);
|
},
|
|
mergeDefaultAndTheme: function (option, ecModel) {
|
var layoutMode = this.layoutMode;
|
var inputPositionParams = layoutMode
|
? layout.getLayoutParams(option) : {};
|
|
var themeModel = ecModel.getTheme();
|
zrUtil.merge(option, themeModel.get(this.mainType));
|
zrUtil.merge(option, this.getDefaultOption());
|
|
if (layoutMode) {
|
layout.mergeLayoutParam(option, inputPositionParams, layoutMode);
|
}
|
},
|
|
mergeOption: function (option, extraOpt) {
|
zrUtil.merge(this.option, option, true);
|
|
var layoutMode = this.layoutMode;
|
if (layoutMode) {
|
layout.mergeLayoutParam(this.option, option, layoutMode);
|
}
|
},
|
|
// Hooker after init or mergeOption
|
optionUpdated: function (newCptOption, isInit) {},
|
|
getDefaultOption: function () {
|
if (!clazzUtil.hasOwn(this, '__defaultOption')) {
|
var optList = [];
|
var Class = this.constructor;
|
while (Class) {
|
var opt = Class.prototype.defaultOption;
|
opt && optList.push(opt);
|
Class = Class.superClass;
|
}
|
|
var defaultOption = {};
|
for (var i = optList.length - 1; i >= 0; i--) {
|
defaultOption = zrUtil.merge(defaultOption, optList[i], true);
|
}
|
clazzUtil.set(this, '__defaultOption', defaultOption);
|
}
|
return clazzUtil.get(this, '__defaultOption');
|
},
|
|
getReferringComponents: function (mainType) {
|
return this.ecModel.queryComponents({
|
mainType: mainType,
|
index: this.get(mainType + 'Index', true),
|
id: this.get(mainType + 'Id', true)
|
});
|
}
|
|
});
|
|
// Reset ComponentModel.extend, add preConstruct.
|
// clazzUtil.enableClassExtend(
|
// ComponentModel,
|
// function (option, parentModel, ecModel, extraOpt) {
|
// // Set dependentModels, componentIndex, name, id, mainType, subType.
|
// zrUtil.extend(this, extraOpt);
|
|
// this.uid = componentUtil.getUID('componentModel');
|
|
// // this.setReadOnly([
|
// // 'type', 'id', 'uid', 'name', 'mainType', 'subType',
|
// // 'dependentModels', 'componentIndex'
|
// // ]);
|
// }
|
// );
|
|
// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
|
clazzUtil.enableClassManagement(
|
ComponentModel, {registerWhenExtend: true}
|
);
|
componentUtil.enableSubTypeDefaulter(ComponentModel);
|
|
// Add capability of ComponentModel.topologicalTravel.
|
componentUtil.enableTopologicalTravel(ComponentModel, getDependencies);
|
|
function getDependencies(componentType) {
|
var deps = [];
|
zrUtil.each(ComponentModel.getClassesByMainType(componentType), function (Clazz) {
|
arrayPush.apply(deps, Clazz.prototype.dependencies || []);
|
});
|
// Ensure main type
|
return zrUtil.map(deps, function (type) {
|
return clazzUtil.parseClassType(type).main;
|
});
|
}
|
|
zrUtil.mixin(ComponentModel, __webpack_require__(22));
|
|
module.exports = ComponentModel;
|
|
|
/***/ },
|
/* 20 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var clazz = __webpack_require__(13);
|
|
var parseClassType = clazz.parseClassType;
|
|
var base = 0;
|
|
var componentUtil = {};
|
|
var DELIMITER = '_';
|
|
/**
|
* @public
|
* @param {string} type
|
* @return {string}
|
*/
|
componentUtil.getUID = function (type) {
|
// Considering the case of crossing js context,
|
// use Math.random to make id as unique as possible.
|
return [(type || ''), base++, Math.random()].join(DELIMITER);
|
};
|
|
/**
|
* @inner
|
*/
|
componentUtil.enableSubTypeDefaulter = function (entity) {
|
|
var subTypeDefaulters = {};
|
|
entity.registerSubTypeDefaulter = function (componentType, defaulter) {
|
componentType = parseClassType(componentType);
|
subTypeDefaulters[componentType.main] = defaulter;
|
};
|
|
entity.determineSubType = function (componentType, option) {
|
var type = option.type;
|
if (!type) {
|
var componentTypeMain = parseClassType(componentType).main;
|
if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) {
|
type = subTypeDefaulters[componentTypeMain](option);
|
}
|
}
|
return type;
|
};
|
|
return entity;
|
};
|
|
/**
|
* Topological travel on Activity Network (Activity On Vertices).
|
* Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis'].
|
*
|
* If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology.
|
*
|
* If there is circle dependencey, Error will be thrown.
|
*
|
*/
|
componentUtil.enableTopologicalTravel = function (entity, dependencyGetter) {
|
|
/**
|
* @public
|
* @param {Array.<string>} targetNameList Target Component type list.
|
* Can be ['aa', 'bb', 'aa.xx']
|
* @param {Array.<string>} fullNameList By which we can build dependency graph.
|
* @param {Function} callback Params: componentType, dependencies.
|
* @param {Object} context Scope of callback.
|
*/
|
entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) {
|
if (!targetNameList.length) {
|
return;
|
}
|
|
var result = makeDepndencyGraph(fullNameList);
|
var graph = result.graph;
|
var stack = result.noEntryList;
|
|
var targetNameSet = {};
|
zrUtil.each(targetNameList, function (name) {
|
targetNameSet[name] = true;
|
});
|
|
while (stack.length) {
|
var currComponentType = stack.pop();
|
var currVertex = graph[currComponentType];
|
var isInTargetNameSet = !!targetNameSet[currComponentType];
|
if (isInTargetNameSet) {
|
callback.call(context, currComponentType, currVertex.originalDeps.slice());
|
delete targetNameSet[currComponentType];
|
}
|
zrUtil.each(
|
currVertex.successor,
|
isInTargetNameSet ? removeEdgeAndAdd : removeEdge
|
);
|
}
|
|
zrUtil.each(targetNameSet, function () {
|
throw new Error('Circle dependency may exists');
|
});
|
|
function removeEdge(succComponentType) {
|
graph[succComponentType].entryCount--;
|
if (graph[succComponentType].entryCount === 0) {
|
stack.push(succComponentType);
|
}
|
}
|
|
// Consider this case: legend depends on series, and we call
|
// chart.setOption({series: [...]}), where only series is in option.
|
// If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will
|
// not be called, but only sereis.mergeOption is called. Thus legend
|
// have no chance to update its local record about series (like which
|
// name of series is available in legend).
|
function removeEdgeAndAdd(succComponentType) {
|
targetNameSet[succComponentType] = true;
|
removeEdge(succComponentType);
|
}
|
};
|
|
/**
|
* DepndencyGraph: {Object}
|
* key: conponentType,
|
* value: {
|
* successor: [conponentTypes...],
|
* originalDeps: [conponentTypes...],
|
* entryCount: {number}
|
* }
|
*/
|
function makeDepndencyGraph(fullNameList) {
|
var graph = {};
|
var noEntryList = [];
|
|
zrUtil.each(fullNameList, function (name) {
|
|
var thisItem = createDependencyGraphItem(graph, name);
|
var originalDeps = thisItem.originalDeps = dependencyGetter(name);
|
|
var availableDeps = getAvailableDependencies(originalDeps, fullNameList);
|
thisItem.entryCount = availableDeps.length;
|
if (thisItem.entryCount === 0) {
|
noEntryList.push(name);
|
}
|
|
zrUtil.each(availableDeps, function (dependentName) {
|
if (zrUtil.indexOf(thisItem.predecessor, dependentName) < 0) {
|
thisItem.predecessor.push(dependentName);
|
}
|
var thatItem = createDependencyGraphItem(graph, dependentName);
|
if (zrUtil.indexOf(thatItem.successor, dependentName) < 0) {
|
thatItem.successor.push(name);
|
}
|
});
|
});
|
|
return {graph: graph, noEntryList: noEntryList};
|
}
|
|
function createDependencyGraphItem(graph, name) {
|
if (!graph[name]) {
|
graph[name] = {predecessor: [], successor: []};
|
}
|
return graph[name];
|
}
|
|
function getAvailableDependencies(originalDeps, fullNameList) {
|
var availableDeps = [];
|
zrUtil.each(originalDeps, function (dep) {
|
zrUtil.indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep);
|
});
|
return availableDeps;
|
}
|
};
|
|
module.exports = componentUtil;
|
|
|
/***/ },
|
/* 21 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// Layout helpers for each component positioning
|
|
|
var zrUtil = __webpack_require__(4);
|
var BoundingRect = __webpack_require__(9);
|
var numberUtil = __webpack_require__(7);
|
var formatUtil = __webpack_require__(6);
|
var parsePercent = numberUtil.parsePercent;
|
var each = zrUtil.each;
|
|
var layout = {};
|
|
/**
|
* @public
|
*/
|
var LOCATION_PARAMS = layout.LOCATION_PARAMS = [
|
'left', 'right', 'top', 'bottom', 'width', 'height'
|
];
|
|
/**
|
* @public
|
*/
|
var HV_NAMES = layout.HV_NAMES = [
|
['width', 'left', 'right'],
|
['height', 'top', 'bottom']
|
];
|
|
function boxLayout(orient, group, gap, maxWidth, maxHeight) {
|
var x = 0;
|
var y = 0;
|
if (maxWidth == null) {
|
maxWidth = Infinity;
|
}
|
if (maxHeight == null) {
|
maxHeight = Infinity;
|
}
|
var currentLineMaxSize = 0;
|
group.eachChild(function (child, idx) {
|
var position = child.position;
|
var rect = child.getBoundingRect();
|
var nextChild = group.childAt(idx + 1);
|
var nextChildRect = nextChild && nextChild.getBoundingRect();
|
var nextX;
|
var nextY;
|
if (orient === 'horizontal') {
|
var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0);
|
nextX = x + moveX;
|
// Wrap when width exceeds maxWidth or meet a `newline` group
|
if (nextX > maxWidth || child.newline) {
|
x = 0;
|
nextX = moveX;
|
y += currentLineMaxSize + gap;
|
currentLineMaxSize = rect.height;
|
}
|
else {
|
currentLineMaxSize = Math.max(currentLineMaxSize, rect.height);
|
}
|
}
|
else {
|
var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0);
|
nextY = y + moveY;
|
// Wrap when width exceeds maxHeight or meet a `newline` group
|
if (nextY > maxHeight || child.newline) {
|
x += currentLineMaxSize + gap;
|
y = 0;
|
nextY = moveY;
|
currentLineMaxSize = rect.width;
|
}
|
else {
|
currentLineMaxSize = Math.max(currentLineMaxSize, rect.width);
|
}
|
}
|
|
if (child.newline) {
|
return;
|
}
|
|
position[0] = x;
|
position[1] = y;
|
|
orient === 'horizontal'
|
? (x = nextX + gap)
|
: (y = nextY + gap);
|
});
|
}
|
|
/**
|
* VBox or HBox layouting
|
* @param {string} orient
|
* @param {module:zrender/container/Group} group
|
* @param {number} gap
|
* @param {number} [width=Infinity]
|
* @param {number} [height=Infinity]
|
*/
|
layout.box = boxLayout;
|
|
/**
|
* VBox layouting
|
* @param {module:zrender/container/Group} group
|
* @param {number} gap
|
* @param {number} [width=Infinity]
|
* @param {number} [height=Infinity]
|
*/
|
layout.vbox = zrUtil.curry(boxLayout, 'vertical');
|
|
/**
|
* HBox layouting
|
* @param {module:zrender/container/Group} group
|
* @param {number} gap
|
* @param {number} [width=Infinity]
|
* @param {number} [height=Infinity]
|
*/
|
layout.hbox = zrUtil.curry(boxLayout, 'horizontal');
|
|
/**
|
* If x or x2 is not specified or 'center' 'left' 'right',
|
* the width would be as long as possible.
|
* If y or y2 is not specified or 'middle' 'top' 'bottom',
|
* the height would be as long as possible.
|
*
|
* @param {Object} positionInfo
|
* @param {number|string} [positionInfo.x]
|
* @param {number|string} [positionInfo.y]
|
* @param {number|string} [positionInfo.x2]
|
* @param {number|string} [positionInfo.y2]
|
* @param {Object} containerRect
|
* @param {string|number} margin
|
* @return {Object} {width, height}
|
*/
|
layout.getAvailableSize = function (positionInfo, containerRect, margin) {
|
var containerWidth = containerRect.width;
|
var containerHeight = containerRect.height;
|
|
var x = parsePercent(positionInfo.x, containerWidth);
|
var y = parsePercent(positionInfo.y, containerHeight);
|
var x2 = parsePercent(positionInfo.x2, containerWidth);
|
var y2 = parsePercent(positionInfo.y2, containerHeight);
|
|
(isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0);
|
(isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth);
|
(isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0);
|
(isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight);
|
|
margin = formatUtil.normalizeCssArray(margin || 0);
|
|
return {
|
width: Math.max(x2 - x - margin[1] - margin[3], 0),
|
height: Math.max(y2 - y - margin[0] - margin[2], 0)
|
};
|
};
|
|
/**
|
* Parse position info.
|
*
|
* @param {Object} positionInfo
|
* @param {number|string} [positionInfo.left]
|
* @param {number|string} [positionInfo.top]
|
* @param {number|string} [positionInfo.right]
|
* @param {number|string} [positionInfo.bottom]
|
* @param {number|string} [positionInfo.width]
|
* @param {number|string} [positionInfo.height]
|
* @param {number|string} [positionInfo.aspect] Aspect is width / height
|
* @param {Object} containerRect
|
* @param {string|number} [margin]
|
*
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
layout.getLayoutRect = function (
|
positionInfo, containerRect, margin
|
) {
|
margin = formatUtil.normalizeCssArray(margin || 0);
|
|
var containerWidth = containerRect.width;
|
var containerHeight = containerRect.height;
|
|
var left = parsePercent(positionInfo.left, containerWidth);
|
var top = parsePercent(positionInfo.top, containerHeight);
|
var right = parsePercent(positionInfo.right, containerWidth);
|
var bottom = parsePercent(positionInfo.bottom, containerHeight);
|
var width = parsePercent(positionInfo.width, containerWidth);
|
var height = parsePercent(positionInfo.height, containerHeight);
|
|
var verticalMargin = margin[2] + margin[0];
|
var horizontalMargin = margin[1] + margin[3];
|
var aspect = positionInfo.aspect;
|
|
// If width is not specified, calculate width from left and right
|
if (isNaN(width)) {
|
width = containerWidth - right - horizontalMargin - left;
|
}
|
if (isNaN(height)) {
|
height = containerHeight - bottom - verticalMargin - top;
|
}
|
|
// If width and height are not given
|
// 1. Graph should not exceeds the container
|
// 2. Aspect must be keeped
|
// 3. Graph should take the space as more as possible
|
if (isNaN(width) && isNaN(height)) {
|
if (aspect > containerWidth / containerHeight) {
|
width = containerWidth * 0.8;
|
}
|
else {
|
height = containerHeight * 0.8;
|
}
|
}
|
|
if (aspect != null) {
|
// Calculate width or height with given aspect
|
if (isNaN(width)) {
|
width = aspect * height;
|
}
|
if (isNaN(height)) {
|
height = width / aspect;
|
}
|
}
|
|
// If left is not specified, calculate left from right and width
|
if (isNaN(left)) {
|
left = containerWidth - right - width - horizontalMargin;
|
}
|
if (isNaN(top)) {
|
top = containerHeight - bottom - height - verticalMargin;
|
}
|
|
// Align left and top
|
switch (positionInfo.left || positionInfo.right) {
|
case 'center':
|
left = containerWidth / 2 - width / 2 - margin[3];
|
break;
|
case 'right':
|
left = containerWidth - width - horizontalMargin;
|
break;
|
}
|
switch (positionInfo.top || positionInfo.bottom) {
|
case 'middle':
|
case 'center':
|
top = containerHeight / 2 - height / 2 - margin[0];
|
break;
|
case 'bottom':
|
top = containerHeight - height - verticalMargin;
|
break;
|
}
|
// If something is wrong and left, top, width, height are calculated as NaN
|
left = left || 0;
|
top = top || 0;
|
if (isNaN(width)) {
|
// Width may be NaN if only one value is given except width
|
width = containerWidth - left - (right || 0);
|
}
|
if (isNaN(height)) {
|
// Height may be NaN if only one value is given except height
|
height = containerHeight - top - (bottom || 0);
|
}
|
|
var rect = new BoundingRect(left + margin[3], top + margin[0], width, height);
|
rect.margin = margin;
|
return rect;
|
};
|
|
|
/**
|
* Position a zr element in viewport
|
* Group position is specified by either
|
* {left, top}, {right, bottom}
|
* If all properties exists, right and bottom will be igonred.
|
*
|
* Logic:
|
* 1. Scale (against origin point in parent coord)
|
* 2. Rotate (against origin point in parent coord)
|
* 3. Traslate (with el.position by this method)
|
* So this method only fixes the last step 'Traslate', which does not affect
|
* scaling and rotating.
|
*
|
* If be called repeatly with the same input el, the same result will be gotten.
|
*
|
* @param {module:zrender/Element} el Should have `getBoundingRect` method.
|
* @param {Object} positionInfo
|
* @param {number|string} [positionInfo.left]
|
* @param {number|string} [positionInfo.top]
|
* @param {number|string} [positionInfo.right]
|
* @param {number|string} [positionInfo.bottom]
|
* @param {Object} containerRect
|
* @param {string|number} margin
|
* @param {Object} [opt]
|
* @param {Array.<number>} [opt.hv=[1,1]] Only horizontal or only vertical.
|
* @param {Array.<number>} [opt.boundingMode='all']
|
* Specify how to calculate boundingRect when locating.
|
* 'all': Position the boundingRect that is transformed and uioned
|
* both itself and its descendants.
|
* This mode simplies confine the elements in the bounding
|
* of their container (e.g., using 'right: 0').
|
* 'raw': Position the boundingRect that is not transformed and only itself.
|
* This mode is useful when you want a element can overflow its
|
* container. (Consider a rotated circle needs to be located in a corner.)
|
* In this mode positionInfo.width/height can only be number.
|
*/
|
layout.positionElement = function (el, positionInfo, containerRect, margin, opt) {
|
var h = !opt || !opt.hv || opt.hv[0];
|
var v = !opt || !opt.hv || opt.hv[1];
|
var boundingMode = opt && opt.boundingMode || 'all';
|
|
if (!h && !v) {
|
return;
|
}
|
|
var rect;
|
if (boundingMode === 'raw') {
|
rect = el.type === 'group'
|
? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0)
|
: el.getBoundingRect();
|
}
|
else {
|
rect = el.getBoundingRect();
|
if (el.needLocalTransform()) {
|
var transform = el.getLocalTransform();
|
// Notice: raw rect may be inner object of el,
|
// which should not be modified.
|
rect = rect.clone();
|
rect.applyTransform(transform);
|
}
|
}
|
|
positionInfo = layout.getLayoutRect(
|
zrUtil.defaults(
|
{width: rect.width, height: rect.height},
|
positionInfo
|
),
|
containerRect,
|
margin
|
);
|
|
// Because 'tranlate' is the last step in transform
|
// (see zrender/core/Transformable#getLocalTransfrom),
|
// we can just only modify el.position to get final result.
|
var elPos = el.position;
|
var dx = h ? positionInfo.x - rect.x : 0;
|
var dy = v ? positionInfo.y - rect.y : 0;
|
|
el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy]);
|
};
|
|
/**
|
* @param {Object} option Contains some of the properties in HV_NAMES.
|
* @param {number} hvIdx 0: horizontal; 1: vertical.
|
*/
|
layout.sizeCalculable = function (option, hvIdx) {
|
return option[HV_NAMES[hvIdx][0]] != null
|
|| (option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null);
|
};
|
|
/**
|
* Consider Case:
|
* When defulat option has {left: 0, width: 100}, and we set {right: 0}
|
* through setOption or media query, using normal zrUtil.merge will cause
|
* {right: 0} does not take effect.
|
*
|
* @example
|
* ComponentModel.extend({
|
* init: function () {
|
* ...
|
* var inputPositionParams = layout.getLayoutParams(option);
|
* this.mergeOption(inputPositionParams);
|
* },
|
* mergeOption: function (newOption) {
|
* newOption && zrUtil.merge(thisOption, newOption, true);
|
* layout.mergeLayoutParam(thisOption, newOption);
|
* }
|
* });
|
*
|
* @param {Object} targetOption
|
* @param {Object} newOption
|
* @param {Object|string} [opt]
|
* @param {boolean|Array.<boolean>} [opt.ignoreSize=false] Some component must has width and height.
|
*/
|
layout.mergeLayoutParam = function (targetOption, newOption, opt) {
|
!zrUtil.isObject(opt) && (opt = {});
|
|
var ignoreSize = opt.ignoreSize;
|
!zrUtil.isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]);
|
|
var hResult = merge(HV_NAMES[0], 0);
|
var vResult = merge(HV_NAMES[1], 1);
|
|
copy(HV_NAMES[0], targetOption, hResult);
|
copy(HV_NAMES[1], targetOption, vResult);
|
|
function merge(names, hvIdx) {
|
var newParams = {};
|
var newValueCount = 0;
|
var merged = {};
|
var mergedValueCount = 0;
|
var enoughParamNumber = 2;
|
|
each(names, function (name) {
|
merged[name] = targetOption[name];
|
});
|
each(names, function (name) {
|
// Consider case: newOption.width is null, which is
|
// set by user for removing width setting.
|
hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]);
|
hasValue(newParams, name) && newValueCount++;
|
hasValue(merged, name) && mergedValueCount++;
|
});
|
|
if (ignoreSize[hvIdx]) {
|
// Only one of left/right is premitted to exist.
|
if (hasValue(newOption, names[1])) {
|
merged[names[2]] = null;
|
}
|
else if (hasValue(newOption, names[2])) {
|
merged[names[1]] = null;
|
}
|
return merged;
|
}
|
|
// Case: newOption: {width: ..., right: ...},
|
// or targetOption: {right: ...} and newOption: {width: ...},
|
// There is no conflict when merged only has params count
|
// little than enoughParamNumber.
|
if (mergedValueCount === enoughParamNumber || !newValueCount) {
|
return merged;
|
}
|
// Case: newOption: {width: ..., right: ...},
|
// Than we can make sure user only want those two, and ignore
|
// all origin params in targetOption.
|
else if (newValueCount >= enoughParamNumber) {
|
return newParams;
|
}
|
else {
|
// Chose another param from targetOption by priority.
|
for (var i = 0; i < names.length; i++) {
|
var name = names[i];
|
if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
|
newParams[name] = targetOption[name];
|
break;
|
}
|
}
|
return newParams;
|
}
|
}
|
|
function hasProp(obj, name) {
|
return obj.hasOwnProperty(name);
|
}
|
|
function hasValue(obj, name) {
|
return obj[name] != null && obj[name] !== 'auto';
|
}
|
|
function copy(names, target, source) {
|
each(names, function (name) {
|
target[name] = source[name];
|
});
|
}
|
};
|
|
/**
|
* Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
|
* @param {Object} source
|
* @return {Object} Result contains those props.
|
*/
|
layout.getLayoutParams = function (source) {
|
return layout.copyLayoutParams({}, source);
|
};
|
|
/**
|
* Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object.
|
* @param {Object} source
|
* @return {Object} Result contains those props.
|
*/
|
layout.copyLayoutParams = function (target, source) {
|
source && target && each(LOCATION_PARAMS, function (name) {
|
source.hasOwnProperty(name) && (target[name] = source[name]);
|
});
|
return target;
|
};
|
|
module.exports = layout;
|
|
|
|
/***/ },
|
/* 22 */
|
/***/ function(module, exports) {
|
|
|
|
module.exports = {
|
getBoxLayoutParams: function () {
|
return {
|
left: this.get('left'),
|
top: this.get('top'),
|
right: this.get('right'),
|
bottom: this.get('bottom'),
|
width: this.get('width'),
|
height: this.get('height')
|
};
|
}
|
};
|
|
|
/***/ },
|
/* 23 */
|
/***/ function(module, exports) {
|
|
|
var platform = '';
|
// Navigator not exists in node
|
if (typeof navigator !== 'undefined') {
|
platform = navigator.platform || '';
|
}
|
module.exports = {
|
// 全图默认背景
|
// backgroundColor: 'rgba(0,0,0,0)',
|
|
// https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization
|
// color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],
|
// 浅色
|
// color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],
|
// color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],
|
// 深色
|
color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'],
|
|
// 默认需要 Grid 配置项
|
// grid: {},
|
// 主题,主题
|
textStyle: {
|
// color: '#000',
|
// decoration: 'none',
|
// PENDING
|
fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif',
|
// fontFamily: 'Arial, Verdana, sans-serif',
|
fontSize: 12,
|
fontStyle: 'normal',
|
fontWeight: 'normal'
|
},
|
|
// http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/
|
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
|
// Default is source-over
|
blendMode: null,
|
|
animation: 'auto',
|
animationDuration: 1000,
|
animationDurationUpdate: 300,
|
animationEasing: 'exponentialOut',
|
animationEasingUpdate: 'cubicOut',
|
|
animationThreshold: 2000,
|
// Configuration for progressive/incremental rendering
|
progressiveThreshold: 3000,
|
progressive: 400,
|
|
// Threshold of if use single hover layer to optimize.
|
// It is recommended that `hoverLayerThreshold` is equivalent to or less than
|
// `progressiveThreshold`, otherwise hover will cause restart of progressive,
|
// which is unexpected.
|
// see example <echarts/test/heatmap-large.html>.
|
hoverLayerThreshold: 3000,
|
|
// See: module:echarts/scale/Time
|
useUTC: false
|
};
|
|
|
/***/ },
|
/* 24 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var classUtil = __webpack_require__(13);
|
var set = classUtil.set;
|
var get = classUtil.get;
|
|
module.exports = {
|
clearColorPalette: function () {
|
set(this, 'colorIdx', 0);
|
set(this, 'colorNameMap', {});
|
},
|
|
getColorFromPalette: function (name, scope) {
|
scope = scope || this;
|
var colorIdx = get(scope, 'colorIdx') || 0;
|
var colorNameMap = get(scope, 'colorNameMap') || set(scope, 'colorNameMap', {});
|
if (colorNameMap[name]) {
|
return colorNameMap[name];
|
}
|
var colorPalette = this.get('color', true) || [];
|
if (!colorPalette.length) {
|
return;
|
}
|
|
var color = colorPalette[colorIdx];
|
if (name) {
|
colorNameMap[name] = color;
|
}
|
set(scope, 'colorIdx', (colorIdx + 1) % colorPalette.length);
|
|
return color;
|
}
|
};
|
|
|
/***/ },
|
/* 25 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var echartsAPIList = [
|
'getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isDisposed',
|
'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption',
|
'getViewOfComponentModel', 'getViewOfSeriesModel'
|
];
|
// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js
|
|
function ExtensionAPI(chartInstance) {
|
zrUtil.each(echartsAPIList, function (name) {
|
this[name] = zrUtil.bind(chartInstance[name], chartInstance);
|
}, this);
|
}
|
|
module.exports = ExtensionAPI;
|
|
|
/***/ },
|
/* 26 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var coordinateSystemCreators = {};
|
|
function CoordinateSystemManager() {
|
|
this._coordinateSystems = [];
|
}
|
|
CoordinateSystemManager.prototype = {
|
|
constructor: CoordinateSystemManager,
|
|
create: function (ecModel, api) {
|
var coordinateSystems = [];
|
zrUtil.each(coordinateSystemCreators, function (creater, type) {
|
var list = creater.create(ecModel, api);
|
coordinateSystems = coordinateSystems.concat(list || []);
|
});
|
|
this._coordinateSystems = coordinateSystems;
|
},
|
|
update: function (ecModel, api) {
|
zrUtil.each(this._coordinateSystems, function (coordSys) {
|
// FIXME MUST have
|
coordSys.update && coordSys.update(ecModel, api);
|
});
|
},
|
|
getCoordinateSystems: function () {
|
return this._coordinateSystems.slice();
|
}
|
};
|
|
CoordinateSystemManager.register = function (type, coordinateSystemCreator) {
|
coordinateSystemCreators[type] = coordinateSystemCreator;
|
};
|
|
CoordinateSystemManager.get = function (type) {
|
return coordinateSystemCreators[type];
|
};
|
|
module.exports = CoordinateSystemManager;
|
|
|
/***/ },
|
/* 27 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* ECharts option manager
|
*
|
* @module {echarts/model/OptionManager}
|
*/
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var ComponentModel = __webpack_require__(19);
|
var each = zrUtil.each;
|
var clone = zrUtil.clone;
|
var map = zrUtil.map;
|
var merge = zrUtil.merge;
|
|
var QUERY_REG = /^(min|max)?(.+)$/;
|
|
/**
|
* TERM EXPLANATIONS:
|
*
|
* [option]:
|
*
|
* An object that contains definitions of components. For example:
|
* var option = {
|
* title: {...},
|
* legend: {...},
|
* visualMap: {...},
|
* series: [
|
* {data: [...]},
|
* {data: [...]},
|
* ...
|
* ]
|
* };
|
*
|
* [rawOption]:
|
*
|
* An object input to echarts.setOption. 'rawOption' may be an
|
* 'option', or may be an object contains multi-options. For example:
|
* var option = {
|
* baseOption: {
|
* title: {...},
|
* legend: {...},
|
* series: [
|
* {data: [...]},
|
* {data: [...]},
|
* ...
|
* ]
|
* },
|
* timeline: {...},
|
* options: [
|
* {title: {...}, series: {data: [...]}},
|
* {title: {...}, series: {data: [...]}},
|
* ...
|
* ],
|
* media: [
|
* {
|
* query: {maxWidth: 320},
|
* option: {series: {x: 20}, visualMap: {show: false}}
|
* },
|
* {
|
* query: {minWidth: 320, maxWidth: 720},
|
* option: {series: {x: 500}, visualMap: {show: true}}
|
* },
|
* {
|
* option: {series: {x: 1200}, visualMap: {show: true}}
|
* }
|
* ]
|
* };
|
*
|
* @alias module:echarts/model/OptionManager
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
function OptionManager(api) {
|
|
/**
|
* @private
|
* @type {module:echarts/ExtensionAPI}
|
*/
|
this._api = api;
|
|
/**
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._timelineOptions = [];
|
|
/**
|
* @private
|
* @type {Array.<Object>}
|
*/
|
this._mediaList = [];
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._mediaDefault;
|
|
/**
|
* -1, means default.
|
* empty means no media.
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._currentMediaIndices = [];
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._optionBackup;
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._newBaseOption;
|
}
|
|
// timeline.notMerge is not supported in ec3. Firstly there is rearly
|
// case that notMerge is needed. Secondly supporting 'notMerge' requires
|
// rawOption cloned and backuped when timeline changed, which does no
|
// good to performance. What's more, that both timeline and setOption
|
// method supply 'notMerge' brings complex and some problems.
|
// Consider this case:
|
// (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);
|
// (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);
|
|
OptionManager.prototype = {
|
|
constructor: OptionManager,
|
|
/**
|
* @public
|
* @param {Object} rawOption Raw option.
|
* @param {module:echarts/model/Global} ecModel
|
* @param {Array.<Function>} optionPreprocessorFuncs
|
* @return {Object} Init option
|
*/
|
setOption: function (rawOption, optionPreprocessorFuncs) {
|
rawOption = clone(rawOption, true);
|
|
// FIXME
|
// 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。
|
|
var oldOptionBackup = this._optionBackup;
|
var newParsedOption = parseRawOption.call(
|
this, rawOption, optionPreprocessorFuncs, !oldOptionBackup
|
);
|
this._newBaseOption = newParsedOption.baseOption;
|
|
// For setOption at second time (using merge mode);
|
if (oldOptionBackup) {
|
// Only baseOption can be merged.
|
mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption);
|
|
// For simplicity, timeline options and media options do not support merge,
|
// that is, if you `setOption` twice and both has timeline options, the latter
|
// timeline opitons will not be merged to the formers, but just substitude them.
|
if (newParsedOption.timelineOptions.length) {
|
oldOptionBackup.timelineOptions = newParsedOption.timelineOptions;
|
}
|
if (newParsedOption.mediaList.length) {
|
oldOptionBackup.mediaList = newParsedOption.mediaList;
|
}
|
if (newParsedOption.mediaDefault) {
|
oldOptionBackup.mediaDefault = newParsedOption.mediaDefault;
|
}
|
}
|
else {
|
this._optionBackup = newParsedOption;
|
}
|
},
|
|
/**
|
* @param {boolean} isRecreate
|
* @return {Object}
|
*/
|
mountOption: function (isRecreate) {
|
var optionBackup = this._optionBackup;
|
|
// TODO
|
// 如果没有reset功能则不clone。
|
|
this._timelineOptions = map(optionBackup.timelineOptions, clone);
|
this._mediaList = map(optionBackup.mediaList, clone);
|
this._mediaDefault = clone(optionBackup.mediaDefault);
|
this._currentMediaIndices = [];
|
|
return clone(isRecreate
|
// this._optionBackup.baseOption, which is created at the first `setOption`
|
// called, and is merged into every new option by inner method `mergeOption`
|
// each time `setOption` called, can be only used in `isRecreate`, because
|
// its reliability is under suspicion. In other cases option merge is
|
// performed by `model.mergeOption`.
|
? optionBackup.baseOption : this._newBaseOption
|
);
|
},
|
|
/**
|
* @param {module:echarts/model/Global} ecModel
|
* @return {Object}
|
*/
|
getTimelineOption: function (ecModel) {
|
var option;
|
var timelineOptions = this._timelineOptions;
|
|
if (timelineOptions.length) {
|
// getTimelineOption can only be called after ecModel inited,
|
// so we can get currentIndex from timelineModel.
|
var timelineModel = ecModel.getComponent('timeline');
|
if (timelineModel) {
|
option = clone(
|
timelineOptions[timelineModel.getCurrentIndex()],
|
true
|
);
|
}
|
}
|
|
return option;
|
},
|
|
/**
|
* @param {module:echarts/model/Global} ecModel
|
* @return {Array.<Object>}
|
*/
|
getMediaOption: function (ecModel) {
|
var ecWidth = this._api.getWidth();
|
var ecHeight = this._api.getHeight();
|
var mediaList = this._mediaList;
|
var mediaDefault = this._mediaDefault;
|
var indices = [];
|
var result = [];
|
|
// No media defined.
|
if (!mediaList.length && !mediaDefault) {
|
return result;
|
}
|
|
// Multi media may be applied, the latter defined media has higher priority.
|
for (var i = 0, len = mediaList.length; i < len; i++) {
|
if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) {
|
indices.push(i);
|
}
|
}
|
|
// FIXME
|
// 是否mediaDefault应该强制用户设置,否则可能修改不能回归。
|
if (!indices.length && mediaDefault) {
|
indices = [-1];
|
}
|
|
if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) {
|
result = map(indices, function (index) {
|
return clone(
|
index === -1 ? mediaDefault.option : mediaList[index].option
|
);
|
});
|
}
|
// Otherwise return nothing.
|
|
this._currentMediaIndices = indices;
|
|
return result;
|
}
|
};
|
|
function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) {
|
var timelineOptions = [];
|
var mediaList = [];
|
var mediaDefault;
|
var baseOption;
|
|
// Compatible with ec2.
|
var timelineOpt = rawOption.timeline;
|
|
if (rawOption.baseOption) {
|
baseOption = rawOption.baseOption;
|
}
|
|
// For timeline
|
if (timelineOpt || rawOption.options) {
|
baseOption = baseOption || {};
|
timelineOptions = (rawOption.options || []).slice();
|
}
|
|
// For media query
|
if (rawOption.media) {
|
baseOption = baseOption || {};
|
var media = rawOption.media;
|
each(media, function (singleMedia) {
|
if (singleMedia && singleMedia.option) {
|
if (singleMedia.query) {
|
mediaList.push(singleMedia);
|
}
|
else if (!mediaDefault) {
|
// Use the first media default.
|
mediaDefault = singleMedia;
|
}
|
}
|
});
|
}
|
|
// For normal option
|
if (!baseOption) {
|
baseOption = rawOption;
|
}
|
|
// Set timelineOpt to baseOption in ec3,
|
// which is convenient for merge option.
|
if (!baseOption.timeline) {
|
baseOption.timeline = timelineOpt;
|
}
|
|
// Preprocess.
|
each([baseOption].concat(timelineOptions)
|
.concat(zrUtil.map(mediaList, function (media) {
|
return media.option;
|
})),
|
function (option) {
|
each(optionPreprocessorFuncs, function (preProcess) {
|
preProcess(option, isNew);
|
});
|
}
|
);
|
|
return {
|
baseOption: baseOption,
|
timelineOptions: timelineOptions,
|
mediaDefault: mediaDefault,
|
mediaList: mediaList
|
};
|
}
|
|
/**
|
* @see <http://www.w3.org/TR/css3-mediaqueries/#media1>
|
* Support: width, height, aspectRatio
|
* Can use max or min as prefix.
|
*/
|
function applyMediaQuery(query, ecWidth, ecHeight) {
|
var realMap = {
|
width: ecWidth,
|
height: ecHeight,
|
aspectratio: ecWidth / ecHeight // lowser case for convenientce.
|
};
|
|
var applicatable = true;
|
|
zrUtil.each(query, function (value, attr) {
|
var matched = attr.match(QUERY_REG);
|
|
if (!matched || !matched[1] || !matched[2]) {
|
return;
|
}
|
|
var operator = matched[1];
|
var realAttr = matched[2].toLowerCase();
|
|
if (!compare(realMap[realAttr], value, operator)) {
|
applicatable = false;
|
}
|
});
|
|
return applicatable;
|
}
|
|
function compare(real, expect, operator) {
|
if (operator === 'min') {
|
return real >= expect;
|
}
|
else if (operator === 'max') {
|
return real <= expect;
|
}
|
else { // Equals
|
return real === expect;
|
}
|
}
|
|
function indicesEquals(indices1, indices2) {
|
// indices is always order by asc and has only finite number.
|
return indices1.join(',') === indices2.join(',');
|
}
|
|
/**
|
* Consider case:
|
* `chart.setOption(opt1);`
|
* Then user do some interaction like dataZoom, dataView changing.
|
* `chart.setOption(opt2);`
|
* Then user press 'reset button' in toolbox.
|
*
|
* After doing that all of the interaction effects should be reset, the
|
* chart should be the same as the result of invoke
|
* `chart.setOption(opt1); chart.setOption(opt2);`.
|
*
|
* Although it is not able ensure that
|
* `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to
|
* `chart.setOption(merge(opt1, opt2));` exactly,
|
* this might be the only simple way to implement that feature.
|
*
|
* MEMO: We've considered some other approaches:
|
* 1. Each model handle its self restoration but not uniform treatment.
|
* (Too complex in logic and error-prone)
|
* 2. Use a shadow ecModel. (Performace expensive)
|
*/
|
function mergeOption(oldOption, newOption) {
|
newOption = newOption || {};
|
|
each(newOption, function (newCptOpt, mainType) {
|
if (newCptOpt == null) {
|
return;
|
}
|
|
var oldCptOpt = oldOption[mainType];
|
|
if (!ComponentModel.hasClass(mainType)) {
|
oldOption[mainType] = merge(oldCptOpt, newCptOpt, true);
|
}
|
else {
|
newCptOpt = modelUtil.normalizeToArray(newCptOpt);
|
oldCptOpt = modelUtil.normalizeToArray(oldCptOpt);
|
|
var mapResult = modelUtil.mappingToExists(oldCptOpt, newCptOpt);
|
|
oldOption[mainType] = map(mapResult, function (item) {
|
return (item.option && item.exist)
|
? merge(item.exist, item.option, true)
|
: (item.exist || item.option);
|
});
|
}
|
});
|
}
|
|
module.exports = OptionManager;
|
|
|
/***/ },
|
/* 28 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var formatUtil = __webpack_require__(6);
|
var classUtil = __webpack_require__(13);
|
var modelUtil = __webpack_require__(5);
|
var ComponentModel = __webpack_require__(19);
|
var colorPaletteMixin = __webpack_require__(24);
|
var env = __webpack_require__(2);
|
var layout = __webpack_require__(21);
|
|
var set = classUtil.set;
|
var get = classUtil.get;
|
var encodeHTML = formatUtil.encodeHTML;
|
var addCommas = formatUtil.addCommas;
|
|
var SeriesModel = ComponentModel.extend({
|
|
type: 'series.__base__',
|
|
/**
|
* @readOnly
|
*/
|
seriesIndex: 0,
|
|
// coodinateSystem will be injected in the echarts/CoordinateSystem
|
coordinateSystem: null,
|
|
/**
|
* @type {Object}
|
* @protected
|
*/
|
defaultOption: null,
|
|
/**
|
* Data provided for legend
|
* @type {Function}
|
*/
|
// PENDING
|
legendDataProvider: null,
|
|
/**
|
* Access path of color for visual
|
*/
|
visualColorAccessPath: 'itemStyle.normal.color',
|
|
/**
|
* Support merge layout params.
|
* Only support 'box' now (left/right/top/bottom/width/height).
|
* @type {string|Object} Object can be {ignoreSize: true}
|
* @readOnly
|
*/
|
layoutMode: null,
|
|
init: function (option, parentModel, ecModel, extraOpt) {
|
|
/**
|
* @type {number}
|
* @readOnly
|
*/
|
this.seriesIndex = this.componentIndex;
|
|
this.mergeDefaultAndTheme(option, ecModel);
|
|
var data = this.getInitialData(option, ecModel);
|
if (true) {
|
zrUtil.assert(data, 'getInitialData returned invalid data.');
|
}
|
/**
|
* @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph}
|
* @private
|
*/
|
set(this, 'dataBeforeProcessed', data);
|
|
// If we reverse the order (make data firstly, and then make
|
// dataBeforeProcessed by cloneShallow), cloneShallow will
|
// cause data.graph.data !== data when using
|
// module:echarts/data/Graph or module:echarts/data/Tree.
|
// See module:echarts/data/helper/linkList
|
this.restoreData();
|
},
|
|
/**
|
* Util for merge default and theme to option
|
* @param {Object} option
|
* @param {module:echarts/model/Global} ecModel
|
*/
|
mergeDefaultAndTheme: function (option, ecModel) {
|
var layoutMode = this.layoutMode;
|
var inputPositionParams = layoutMode
|
? layout.getLayoutParams(option) : {};
|
|
zrUtil.merge(
|
option,
|
ecModel.getTheme().get(this.subType)
|
);
|
zrUtil.merge(option, this.getDefaultOption());
|
|
// Default label emphasis `position` and `show`
|
// FIXME Set label in mergeOption
|
modelUtil.defaultEmphasis(option.label, modelUtil.LABEL_OPTIONS);
|
|
this.fillDataTextStyle(option.data);
|
|
if (layoutMode) {
|
layout.mergeLayoutParam(option, inputPositionParams, layoutMode);
|
}
|
},
|
|
mergeOption: function (newSeriesOption, ecModel) {
|
newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true);
|
this.fillDataTextStyle(newSeriesOption.data);
|
|
var layoutMode = this.layoutMode;
|
if (layoutMode) {
|
layout.mergeLayoutParam(this.option, newSeriesOption, layoutMode);
|
}
|
|
var data = this.getInitialData(newSeriesOption, ecModel);
|
// TODO Merge data?
|
if (data) {
|
set(this, 'data', data);
|
set(this, 'dataBeforeProcessed', data.cloneShallow());
|
}
|
},
|
|
fillDataTextStyle: function (data) {
|
// Default data label emphasis `position` and `show`
|
// FIXME Tree structure data ?
|
// FIXME Performance ?
|
if (data) {
|
for (var i = 0; i < data.length; i++) {
|
if (data[i] && data[i].label) {
|
modelUtil.defaultEmphasis(data[i].label, modelUtil.LABEL_OPTIONS);
|
}
|
}
|
}
|
},
|
|
/**
|
* Init a data structure from data related option in series
|
* Must be overwritten
|
*/
|
getInitialData: function () {},
|
|
/**
|
* @param {string} [dataType]
|
* @return {module:echarts/data/List}
|
*/
|
getData: function (dataType) {
|
var data = get(this, 'data');
|
return dataType == null ? data : data.getLinkedData(dataType);
|
},
|
|
/**
|
* @param {module:echarts/data/List} data
|
*/
|
setData: function (data) {
|
set(this, 'data', data);
|
},
|
|
/**
|
* Get data before processed
|
* @return {module:echarts/data/List}
|
*/
|
getRawData: function () {
|
return get(this, 'dataBeforeProcessed');
|
},
|
|
/**
|
* Coord dimension to data dimension.
|
*
|
* By default the result is the same as dimensions of series data.
|
* But in some series data dimensions are different from coord dimensions (i.e.
|
* candlestick and boxplot). Override this method to handle those cases.
|
*
|
* Coord dimension to data dimension can be one-to-many
|
*
|
* @param {string} coordDim
|
* @return {Array.<string>} dimensions on the axis.
|
*/
|
coordDimToDataDim: function (coordDim) {
|
return [coordDim];
|
},
|
|
/**
|
* Convert data dimension to coord dimension.
|
*
|
* @param {string|number} dataDim
|
* @return {string}
|
*/
|
dataDimToCoordDim: function (dataDim) {
|
return dataDim;
|
},
|
|
/**
|
* Get base axis if has coordinate system and has axis.
|
* By default use coordSys.getBaseAxis();
|
* Can be overrided for some chart.
|
* @return {type} description
|
*/
|
getBaseAxis: function () {
|
var coordSys = this.coordinateSystem;
|
return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis();
|
},
|
|
// FIXME
|
/**
|
* Default tooltip formatter
|
*
|
* @param {number} dataIndex
|
* @param {boolean} [multipleSeries=false]
|
* @param {number} [dataType]
|
*/
|
formatTooltip: function (dataIndex, multipleSeries, dataType) {
|
function formatArrayValue(value) {
|
var result = [];
|
|
zrUtil.each(value, function (val, idx) {
|
var dimInfo = data.getDimensionInfo(idx);
|
var dimType = dimInfo && dimInfo.type;
|
var valStr;
|
|
if (dimType === 'ordinal') {
|
valStr = val + '';
|
}
|
else if (dimType === 'time') {
|
valStr = multipleSeries ? '' : formatUtil.formatTime('yyyy/MM/dd hh:mm:ss', val);
|
}
|
else {
|
valStr = addCommas(val);
|
}
|
|
valStr && result.push(valStr);
|
});
|
|
return result.join(', ');
|
}
|
|
var data = get(this, 'data');
|
|
var value = this.getRawValue(dataIndex);
|
var formattedValue = encodeHTML(
|
zrUtil.isArray(value) ? formatArrayValue(value) : addCommas(value)
|
);
|
var name = data.getName(dataIndex);
|
|
var color = data.getItemVisual(dataIndex, 'color');
|
if (zrUtil.isObject(color) && color.colorStops) {
|
color = (color.colorStops[0] || {}).color;
|
}
|
color = color || 'transparent';
|
|
var colorEl = '<span style="display:inline-block;margin-right:5px;'
|
+ 'border-radius:10px;width:9px;height:9px;background-color:' + encodeHTML(color) + '"></span>';
|
|
var seriesName = this.name;
|
// FIXME
|
if (seriesName === '\0-') {
|
// Not show '-'
|
seriesName = '';
|
}
|
return !multipleSeries
|
? ((seriesName && encodeHTML(seriesName) + '<br />') + colorEl
|
+ (name
|
? encodeHTML(name) + ' : ' + formattedValue
|
: formattedValue
|
)
|
)
|
: (colorEl + encodeHTML(this.name) + ' : ' + formattedValue);
|
},
|
|
/**
|
* @return {boolean}
|
*/
|
isAnimationEnabled: function () {
|
if (env.node) {
|
return false;
|
}
|
|
var animationEnabled = this.getShallow('animation');
|
if (animationEnabled) {
|
if (this.getData().count() > this.getShallow('animationThreshold')) {
|
animationEnabled = false;
|
}
|
}
|
return animationEnabled;
|
},
|
|
restoreData: function () {
|
set(this, 'data', get(this, 'dataBeforeProcessed').cloneShallow());
|
},
|
|
getColorFromPalette: function (name, scope) {
|
var ecModel = this.ecModel;
|
// PENDING
|
var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope);
|
if (!color) {
|
color = ecModel.getColorFromPalette(name, scope);
|
}
|
return color;
|
},
|
|
/**
|
* Get data indices for show tooltip content. See tooltip.
|
* @abstract
|
* @param {Array.<string>|string} dim
|
* @param {Array.<number>} value
|
* @param {module:echarts/coord/single/SingleAxis} baseAxis
|
* @return {Object} {dataIndices, nestestValue}.
|
*/
|
getAxisTooltipData: null,
|
|
/**
|
* See tooltip.
|
* @abstract
|
* @param {number} dataIndex
|
* @return {Array.<number>} Point of tooltip. null/undefined can be returned.
|
*/
|
getTooltipPosition: null
|
});
|
|
zrUtil.mixin(SeriesModel, modelUtil.dataFormatMixin);
|
zrUtil.mixin(SeriesModel, colorPaletteMixin);
|
|
module.exports = SeriesModel;
|
|
|
/***/ },
|
/* 29 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var Group = __webpack_require__(30);
|
var componentUtil = __webpack_require__(20);
|
var clazzUtil = __webpack_require__(13);
|
|
var Component = function () {
|
/**
|
* @type {module:zrender/container/Group}
|
* @readOnly
|
*/
|
this.group = new Group();
|
|
/**
|
* @type {string}
|
* @readOnly
|
*/
|
this.uid = componentUtil.getUID('viewComponent');
|
};
|
|
Component.prototype = {
|
|
constructor: Component,
|
|
init: function (ecModel, api) {},
|
|
render: function (componentModel, ecModel, api, payload) {},
|
|
dispose: function () {}
|
|
};
|
|
var componentProto = Component.prototype;
|
componentProto.updateView
|
= componentProto.updateLayout
|
= componentProto.updateVisual
|
= function (seriesModel, ecModel, api, payload) {
|
// Do nothing;
|
};
|
// Enable Component.extend.
|
clazzUtil.enableClassExtend(Component);
|
|
// Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
|
clazzUtil.enableClassManagement(Component, {registerWhenExtend: true});
|
|
module.exports = Component;
|
|
|
/***/ },
|
/* 30 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
|
* @module zrender/graphic/Group
|
* @example
|
* var Group = require('zrender/lib/container/Group');
|
* var Circle = require('zrender/lib/graphic/shape/Circle');
|
* var g = new Group();
|
* g.position[0] = 100;
|
* g.position[1] = 100;
|
* g.add(new Circle({
|
* style: {
|
* x: 100,
|
* y: 100,
|
* r: 20,
|
* }
|
* }));
|
* zr.add(g);
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var Element = __webpack_require__(31);
|
var BoundingRect = __webpack_require__(9);
|
|
/**
|
* @alias module:zrender/graphic/Group
|
* @constructor
|
* @extends module:zrender/mixin/Transformable
|
* @extends module:zrender/mixin/Eventful
|
*/
|
var Group = function (opts) {
|
|
opts = opts || {};
|
|
Element.call(this, opts);
|
|
for (var key in opts) {
|
if (opts.hasOwnProperty(key)) {
|
this[key] = opts[key];
|
}
|
}
|
|
this._children = [];
|
|
this.__storage = null;
|
|
this.__dirty = true;
|
};
|
|
Group.prototype = {
|
|
constructor: Group,
|
|
isGroup: true,
|
|
/**
|
* @type {string}
|
*/
|
type: 'group',
|
|
/**
|
* 所有子孙元素是否响应鼠标事件
|
* @name module:/zrender/container/Group#silent
|
* @type {boolean}
|
* @default false
|
*/
|
silent: false,
|
|
/**
|
* @return {Array.<module:zrender/Element>}
|
*/
|
children: function () {
|
return this._children.slice();
|
},
|
|
/**
|
* 获取指定 index 的儿子节点
|
* @param {number} idx
|
* @return {module:zrender/Element}
|
*/
|
childAt: function (idx) {
|
return this._children[idx];
|
},
|
|
/**
|
* 获取指定名字的儿子节点
|
* @param {string} name
|
* @return {module:zrender/Element}
|
*/
|
childOfName: function (name) {
|
var children = this._children;
|
for (var i = 0; i < children.length; i++) {
|
if (children[i].name === name) {
|
return children[i];
|
}
|
}
|
},
|
|
/**
|
* @return {number}
|
*/
|
childCount: function () {
|
return this._children.length;
|
},
|
|
/**
|
* 添加子节点到最后
|
* @param {module:zrender/Element} child
|
*/
|
add: function (child) {
|
if (child && child !== this && child.parent !== this) {
|
|
this._children.push(child);
|
|
this._doAdd(child);
|
}
|
|
return this;
|
},
|
|
/**
|
* 添加子节点在 nextSibling 之前
|
* @param {module:zrender/Element} child
|
* @param {module:zrender/Element} nextSibling
|
*/
|
addBefore: function (child, nextSibling) {
|
if (child && child !== this && child.parent !== this
|
&& nextSibling && nextSibling.parent === this) {
|
|
var children = this._children;
|
var idx = children.indexOf(nextSibling);
|
|
if (idx >= 0) {
|
children.splice(idx, 0, child);
|
this._doAdd(child);
|
}
|
}
|
|
return this;
|
},
|
|
_doAdd: function (child) {
|
if (child.parent) {
|
child.parent.remove(child);
|
}
|
|
child.parent = this;
|
|
var storage = this.__storage;
|
var zr = this.__zr;
|
if (storage && storage !== child.__storage) {
|
|
storage.addToStorage(child);
|
|
if (child instanceof Group) {
|
child.addChildrenToStorage(storage);
|
}
|
}
|
|
zr && zr.refresh();
|
},
|
|
/**
|
* 移除子节点
|
* @param {module:zrender/Element} child
|
*/
|
remove: function (child) {
|
var zr = this.__zr;
|
var storage = this.__storage;
|
var children = this._children;
|
|
var idx = zrUtil.indexOf(children, child);
|
if (idx < 0) {
|
return this;
|
}
|
children.splice(idx, 1);
|
|
child.parent = null;
|
|
if (storage) {
|
|
storage.delFromStorage(child);
|
|
if (child instanceof Group) {
|
child.delChildrenFromStorage(storage);
|
}
|
}
|
|
zr && zr.refresh();
|
|
return this;
|
},
|
|
/**
|
* 移除所有子节点
|
*/
|
removeAll: function () {
|
var children = this._children;
|
var storage = this.__storage;
|
var child;
|
var i;
|
for (i = 0; i < children.length; i++) {
|
child = children[i];
|
if (storage) {
|
storage.delFromStorage(child);
|
if (child instanceof Group) {
|
child.delChildrenFromStorage(storage);
|
}
|
}
|
child.parent = null;
|
}
|
children.length = 0;
|
|
return this;
|
},
|
|
/**
|
* 遍历所有子节点
|
* @param {Function} cb
|
* @param {} context
|
*/
|
eachChild: function (cb, context) {
|
var children = this._children;
|
for (var i = 0; i < children.length; i++) {
|
var child = children[i];
|
cb.call(context, child, i);
|
}
|
return this;
|
},
|
|
/**
|
* 深度优先遍历所有子孙节点
|
* @param {Function} cb
|
* @param {} context
|
*/
|
traverse: function (cb, context) {
|
for (var i = 0; i < this._children.length; i++) {
|
var child = this._children[i];
|
cb.call(context, child);
|
|
if (child.type === 'group') {
|
child.traverse(cb, context);
|
}
|
}
|
return this;
|
},
|
|
addChildrenToStorage: function (storage) {
|
for (var i = 0; i < this._children.length; i++) {
|
var child = this._children[i];
|
storage.addToStorage(child);
|
if (child instanceof Group) {
|
child.addChildrenToStorage(storage);
|
}
|
}
|
},
|
|
delChildrenFromStorage: function (storage) {
|
for (var i = 0; i < this._children.length; i++) {
|
var child = this._children[i];
|
storage.delFromStorage(child);
|
if (child instanceof Group) {
|
child.delChildrenFromStorage(storage);
|
}
|
}
|
},
|
|
dirty: function () {
|
this.__dirty = true;
|
this.__zr && this.__zr.refresh();
|
return this;
|
},
|
|
/**
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
getBoundingRect: function (includeChildren) {
|
// TODO Caching
|
var rect = null;
|
var tmpRect = new BoundingRect(0, 0, 0, 0);
|
var children = includeChildren || this._children;
|
var tmpMat = [];
|
|
for (var i = 0; i < children.length; i++) {
|
var child = children[i];
|
if (child.ignore || child.invisible) {
|
continue;
|
}
|
|
var childRect = child.getBoundingRect();
|
var transform = child.getLocalTransform(tmpMat);
|
// TODO
|
// The boundingRect cacluated by transforming original
|
// rect may be bigger than the actual bundingRect when rotation
|
// is used. (Consider a circle rotated aginst its center, where
|
// the actual boundingRect should be the same as that not be
|
// rotated.) But we can not find better approach to calculate
|
// actual boundingRect yet, considering performance.
|
if (transform) {
|
tmpRect.copy(childRect);
|
tmpRect.applyTransform(transform);
|
rect = rect || tmpRect.clone();
|
rect.union(tmpRect);
|
}
|
else {
|
rect = rect || childRect.clone();
|
rect.union(childRect);
|
}
|
}
|
return rect || tmpRect;
|
}
|
};
|
|
zrUtil.inherits(Group, Element);
|
|
module.exports = Group;
|
|
|
/***/ },
|
/* 31 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* @module zrender/Element
|
*/
|
|
|
var guid = __webpack_require__(32);
|
var Eventful = __webpack_require__(33);
|
var Transformable = __webpack_require__(34);
|
var Animatable = __webpack_require__(35);
|
var zrUtil = __webpack_require__(4);
|
|
/**
|
* @alias module:zrender/Element
|
* @constructor
|
* @extends {module:zrender/mixin/Animatable}
|
* @extends {module:zrender/mixin/Transformable}
|
* @extends {module:zrender/mixin/Eventful}
|
*/
|
var Element = function (opts) {
|
|
Transformable.call(this, opts);
|
Eventful.call(this, opts);
|
Animatable.call(this, opts);
|
|
/**
|
* 画布元素ID
|
* @type {string}
|
*/
|
this.id = opts.id || guid();
|
};
|
|
Element.prototype = {
|
|
/**
|
* 元素类型
|
* Element type
|
* @type {string}
|
*/
|
type: 'element',
|
|
/**
|
* 元素名字
|
* Element name
|
* @type {string}
|
*/
|
name: '',
|
|
/**
|
* ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值
|
* ZRender instance will be assigned when element is associated with zrender
|
* @name module:/zrender/Element#__zr
|
* @type {module:zrender/ZRender}
|
*/
|
__zr: null,
|
|
/**
|
* 图形是否忽略,为true时忽略图形的绘制以及事件触发
|
* If ignore drawing and events of the element object
|
* @name module:/zrender/Element#ignore
|
* @type {boolean}
|
* @default false
|
*/
|
ignore: false,
|
|
/**
|
* 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪
|
* 该路径会继承被裁减对象的变换
|
* @type {module:zrender/graphic/Path}
|
* @see http://www.w3.org/TR/2dcontext/#clipping-region
|
* @readOnly
|
*/
|
clipPath: null,
|
|
/**
|
* Drift element
|
* @param {number} dx dx on the global space
|
* @param {number} dy dy on the global space
|
*/
|
drift: function (dx, dy) {
|
switch (this.draggable) {
|
case 'horizontal':
|
dy = 0;
|
break;
|
case 'vertical':
|
dx = 0;
|
break;
|
}
|
|
var m = this.transform;
|
if (!m) {
|
m = this.transform = [1, 0, 0, 1, 0, 0];
|
}
|
m[4] += dx;
|
m[5] += dy;
|
|
this.decomposeTransform();
|
this.dirty(false);
|
},
|
|
/**
|
* Hook before update
|
*/
|
beforeUpdate: function () {},
|
/**
|
* Hook after update
|
*/
|
afterUpdate: function () {},
|
/**
|
* Update each frame
|
*/
|
update: function () {
|
this.updateTransform();
|
},
|
|
/**
|
* @param {Function} cb
|
* @param {} context
|
*/
|
traverse: function (cb, context) {},
|
|
/**
|
* @protected
|
*/
|
attrKV: function (key, value) {
|
if (key === 'position' || key === 'scale' || key === 'origin') {
|
// Copy the array
|
if (value) {
|
var target = this[key];
|
if (!target) {
|
target = this[key] = [];
|
}
|
target[0] = value[0];
|
target[1] = value[1];
|
}
|
}
|
else {
|
this[key] = value;
|
}
|
},
|
|
/**
|
* Hide the element
|
*/
|
hide: function () {
|
this.ignore = true;
|
this.__zr && this.__zr.refresh();
|
},
|
|
/**
|
* Show the element
|
*/
|
show: function () {
|
this.ignore = false;
|
this.__zr && this.__zr.refresh();
|
},
|
|
/**
|
* @param {string|Object} key
|
* @param {*} value
|
*/
|
attr: function (key, value) {
|
if (typeof key === 'string') {
|
this.attrKV(key, value);
|
}
|
else if (zrUtil.isObject(key)) {
|
for (var name in key) {
|
if (key.hasOwnProperty(name)) {
|
this.attrKV(name, key[name]);
|
}
|
}
|
}
|
|
this.dirty(false);
|
|
return this;
|
},
|
|
/**
|
* @param {module:zrender/graphic/Path} clipPath
|
*/
|
setClipPath: function (clipPath) {
|
var zr = this.__zr;
|
if (zr) {
|
clipPath.addSelfToZr(zr);
|
}
|
|
// Remove previous clip path
|
if (this.clipPath && this.clipPath !== clipPath) {
|
this.removeClipPath();
|
}
|
|
this.clipPath = clipPath;
|
clipPath.__zr = zr;
|
clipPath.__clipTarget = this;
|
|
this.dirty(false);
|
},
|
|
/**
|
*/
|
removeClipPath: function () {
|
var clipPath = this.clipPath;
|
if (clipPath) {
|
if (clipPath.__zr) {
|
clipPath.removeSelfFromZr(clipPath.__zr);
|
}
|
|
clipPath.__zr = null;
|
clipPath.__clipTarget = null;
|
this.clipPath = null;
|
|
this.dirty(false);
|
}
|
},
|
|
/**
|
* Add self from zrender instance.
|
* Not recursively because it will be invoked when element added to storage.
|
* @param {module:zrender/ZRender} zr
|
*/
|
addSelfToZr: function (zr) {
|
this.__zr = zr;
|
// 添加动画
|
var animators = this.animators;
|
if (animators) {
|
for (var i = 0; i < animators.length; i++) {
|
zr.animation.addAnimator(animators[i]);
|
}
|
}
|
|
if (this.clipPath) {
|
this.clipPath.addSelfToZr(zr);
|
}
|
},
|
|
/**
|
* Remove self from zrender instance.
|
* Not recursively because it will be invoked when element added to storage.
|
* @param {module:zrender/ZRender} zr
|
*/
|
removeSelfFromZr: function (zr) {
|
this.__zr = null;
|
// 移除动画
|
var animators = this.animators;
|
if (animators) {
|
for (var i = 0; i < animators.length; i++) {
|
zr.animation.removeAnimator(animators[i]);
|
}
|
}
|
|
if (this.clipPath) {
|
this.clipPath.removeSelfFromZr(zr);
|
}
|
}
|
};
|
|
zrUtil.mixin(Element, Animatable);
|
zrUtil.mixin(Element, Transformable);
|
zrUtil.mixin(Element, Eventful);
|
|
module.exports = Element;
|
|
|
/***/ },
|
/* 32 */
|
/***/ function(module, exports) {
|
|
/**
|
* zrender: 生成唯一id
|
*
|
* @author errorrik (errorrik@gmail.com)
|
*/
|
|
|
var idStart = 0x0907;
|
|
module.exports = function () {
|
return idStart++;
|
};
|
|
|
|
/***/ },
|
/* 33 */
|
/***/ function(module, exports) {
|
|
/**
|
* 事件扩展
|
* @module zrender/mixin/Eventful
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* pissang (https://www.github.com/pissang)
|
*/
|
|
|
var arrySlice = Array.prototype.slice;
|
|
/**
|
* 事件分发器
|
* @alias module:zrender/mixin/Eventful
|
* @constructor
|
*/
|
var Eventful = function () {
|
this._$handlers = {};
|
};
|
|
Eventful.prototype = {
|
|
constructor: Eventful,
|
|
/**
|
* 单次触发绑定,trigger后销毁
|
*
|
* @param {string} event 事件名
|
* @param {Function} handler 响应函数
|
* @param {Object} context
|
*/
|
one: function (event, handler, context) {
|
var _h = this._$handlers;
|
|
if (!handler || !event) {
|
return this;
|
}
|
|
if (!_h[event]) {
|
_h[event] = [];
|
}
|
|
for (var i = 0; i < _h[event].length; i++) {
|
if (_h[event][i].h === handler) {
|
return this;
|
}
|
}
|
|
_h[event].push({
|
h: handler,
|
one: true,
|
ctx: context || this
|
});
|
|
return this;
|
},
|
|
/**
|
* 绑定事件
|
* @param {string} event 事件名
|
* @param {Function} handler 事件处理函数
|
* @param {Object} [context]
|
*/
|
on: function (event, handler, context) {
|
var _h = this._$handlers;
|
|
if (!handler || !event) {
|
return this;
|
}
|
|
if (!_h[event]) {
|
_h[event] = [];
|
}
|
|
for (var i = 0; i < _h[event].length; i++) {
|
if (_h[event][i].h === handler) {
|
return this;
|
}
|
}
|
|
_h[event].push({
|
h: handler,
|
one: false,
|
ctx: context || this
|
});
|
|
return this;
|
},
|
|
/**
|
* 是否绑定了事件
|
* @param {string} event
|
* @return {boolean}
|
*/
|
isSilent: function (event) {
|
var _h = this._$handlers;
|
return _h[event] && _h[event].length;
|
},
|
|
/**
|
* 解绑事件
|
* @param {string} event 事件名
|
* @param {Function} [handler] 事件处理函数
|
*/
|
off: function (event, handler) {
|
var _h = this._$handlers;
|
|
if (!event) {
|
this._$handlers = {};
|
return this;
|
}
|
|
if (handler) {
|
if (_h[event]) {
|
var newList = [];
|
for (var i = 0, l = _h[event].length; i < l; i++) {
|
if (_h[event][i]['h'] != handler) {
|
newList.push(_h[event][i]);
|
}
|
}
|
_h[event] = newList;
|
}
|
|
if (_h[event] && _h[event].length === 0) {
|
delete _h[event];
|
}
|
}
|
else {
|
delete _h[event];
|
}
|
|
return this;
|
},
|
|
/**
|
* 事件分发
|
*
|
* @param {string} type 事件类型
|
*/
|
trigger: function (type) {
|
if (this._$handlers[type]) {
|
var args = arguments;
|
var argLen = args.length;
|
|
if (argLen > 3) {
|
args = arrySlice.call(args, 1);
|
}
|
|
var _h = this._$handlers[type];
|
var len = _h.length;
|
for (var i = 0; i < len;) {
|
// Optimize advise from backbone
|
switch (argLen) {
|
case 1:
|
_h[i]['h'].call(_h[i]['ctx']);
|
break;
|
case 2:
|
_h[i]['h'].call(_h[i]['ctx'], args[1]);
|
break;
|
case 3:
|
_h[i]['h'].call(_h[i]['ctx'], args[1], args[2]);
|
break;
|
default:
|
// have more than 2 given arguments
|
_h[i]['h'].apply(_h[i]['ctx'], args);
|
break;
|
}
|
|
if (_h[i]['one']) {
|
_h.splice(i, 1);
|
len--;
|
}
|
else {
|
i++;
|
}
|
}
|
}
|
|
return this;
|
},
|
|
/**
|
* 带有context的事件分发, 最后一个参数是事件回调的context
|
* @param {string} type 事件类型
|
*/
|
triggerWithContext: function (type) {
|
if (this._$handlers[type]) {
|
var args = arguments;
|
var argLen = args.length;
|
|
if (argLen > 4) {
|
args = arrySlice.call(args, 1, args.length - 1);
|
}
|
var ctx = args[args.length - 1];
|
|
var _h = this._$handlers[type];
|
var len = _h.length;
|
for (var i = 0; i < len;) {
|
// Optimize advise from backbone
|
switch (argLen) {
|
case 1:
|
_h[i]['h'].call(ctx);
|
break;
|
case 2:
|
_h[i]['h'].call(ctx, args[1]);
|
break;
|
case 3:
|
_h[i]['h'].call(ctx, args[1], args[2]);
|
break;
|
default:
|
// have more than 2 given arguments
|
_h[i]['h'].apply(ctx, args);
|
break;
|
}
|
|
if (_h[i]['one']) {
|
_h.splice(i, 1);
|
len--;
|
}
|
else {
|
i++;
|
}
|
}
|
}
|
|
return this;
|
}
|
};
|
|
// 对象可以通过 onxxxx 绑定事件
|
/**
|
* @event module:zrender/mixin/Eventful#onclick
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmouseover
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmouseout
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmousemove
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmousewheel
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmousedown
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#onmouseup
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondrag
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondragstart
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondragend
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondragenter
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondragleave
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondragover
|
* @type {Function}
|
* @default null
|
*/
|
/**
|
* @event module:zrender/mixin/Eventful#ondrop
|
* @type {Function}
|
* @default null
|
*/
|
|
module.exports = Eventful;
|
|
|
|
/***/ },
|
/* 34 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 提供变换扩展
|
* @module zrender/mixin/Transformable
|
* @author pissang (https://www.github.com/pissang)
|
*/
|
|
|
var matrix = __webpack_require__(11);
|
var vector = __webpack_require__(10);
|
var mIdentity = matrix.identity;
|
|
var EPSILON = 5e-5;
|
|
function isNotAroundZero(val) {
|
return val > EPSILON || val < -EPSILON;
|
}
|
|
/**
|
* @alias module:zrender/mixin/Transformable
|
* @constructor
|
*/
|
var Transformable = function (opts) {
|
opts = opts || {};
|
// If there are no given position, rotation, scale
|
if (!opts.position) {
|
/**
|
* 平移
|
* @type {Array.<number>}
|
* @default [0, 0]
|
*/
|
this.position = [0, 0];
|
}
|
if (opts.rotation == null) {
|
/**
|
* 旋转
|
* @type {Array.<number>}
|
* @default 0
|
*/
|
this.rotation = 0;
|
}
|
if (!opts.scale) {
|
/**
|
* 缩放
|
* @type {Array.<number>}
|
* @default [1, 1]
|
*/
|
this.scale = [1, 1];
|
}
|
/**
|
* 旋转和缩放的原点
|
* @type {Array.<number>}
|
* @default null
|
*/
|
this.origin = this.origin || null;
|
};
|
|
var transformableProto = Transformable.prototype;
|
transformableProto.transform = null;
|
|
/**
|
* 判断是否需要有坐标变换
|
* 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
|
*/
|
transformableProto.needLocalTransform = function () {
|
return isNotAroundZero(this.rotation)
|
|| isNotAroundZero(this.position[0])
|
|| isNotAroundZero(this.position[1])
|
|| isNotAroundZero(this.scale[0] - 1)
|
|| isNotAroundZero(this.scale[1] - 1);
|
};
|
|
transformableProto.updateTransform = function () {
|
var parent = this.parent;
|
var parentHasTransform = parent && parent.transform;
|
var needLocalTransform = this.needLocalTransform();
|
|
var m = this.transform;
|
if (!(needLocalTransform || parentHasTransform)) {
|
m && mIdentity(m);
|
return;
|
}
|
|
m = m || matrix.create();
|
|
if (needLocalTransform) {
|
this.getLocalTransform(m);
|
}
|
else {
|
mIdentity(m);
|
}
|
|
// 应用父节点变换
|
if (parentHasTransform) {
|
if (needLocalTransform) {
|
matrix.mul(m, parent.transform, m);
|
}
|
else {
|
matrix.copy(m, parent.transform);
|
}
|
}
|
// 保存这个变换矩阵
|
this.transform = m;
|
|
this.invTransform = this.invTransform || matrix.create();
|
matrix.invert(this.invTransform, m);
|
};
|
|
transformableProto.getLocalTransform = function (m) {
|
return Transformable.getLocalTransform(this, m);
|
};
|
|
/**
|
* 将自己的transform应用到context上
|
* @param {Context2D} ctx
|
*/
|
transformableProto.setTransform = function (ctx) {
|
var m = this.transform;
|
var dpr = ctx.dpr || 1;
|
if (m) {
|
ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
|
}
|
else {
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
}
|
};
|
|
transformableProto.restoreTransform = function (ctx) {
|
var dpr = ctx.dpr || 1;
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
};
|
|
var tmpTransform = [];
|
|
/**
|
* 分解`transform`矩阵到`position`, `rotation`, `scale`
|
*/
|
transformableProto.decomposeTransform = function () {
|
if (!this.transform) {
|
return;
|
}
|
var parent = this.parent;
|
var m = this.transform;
|
if (parent && parent.transform) {
|
// Get local transform and decompose them to position, scale, rotation
|
matrix.mul(tmpTransform, parent.invTransform, m);
|
m = tmpTransform;
|
}
|
var sx = m[0] * m[0] + m[1] * m[1];
|
var sy = m[2] * m[2] + m[3] * m[3];
|
var position = this.position;
|
var scale = this.scale;
|
if (isNotAroundZero(sx - 1)) {
|
sx = Math.sqrt(sx);
|
}
|
if (isNotAroundZero(sy - 1)) {
|
sy = Math.sqrt(sy);
|
}
|
if (m[0] < 0) {
|
sx = -sx;
|
}
|
if (m[3] < 0) {
|
sy = -sy;
|
}
|
position[0] = m[4];
|
position[1] = m[5];
|
scale[0] = sx;
|
scale[1] = sy;
|
this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
|
};
|
|
/**
|
* Get global scale
|
* @return {Array.<number>}
|
*/
|
transformableProto.getGlobalScale = function () {
|
var m = this.transform;
|
if (!m) {
|
return [1, 1];
|
}
|
var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
|
var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
|
if (m[0] < 0) {
|
sx = -sx;
|
}
|
if (m[3] < 0) {
|
sy = -sy;
|
}
|
return [sx, sy];
|
};
|
/**
|
* 变换坐标位置到 shape 的局部坐标空间
|
* @method
|
* @param {number} x
|
* @param {number} y
|
* @return {Array.<number>}
|
*/
|
transformableProto.transformCoordToLocal = function (x, y) {
|
var v2 = [x, y];
|
var invTransform = this.invTransform;
|
if (invTransform) {
|
vector.applyTransform(v2, v2, invTransform);
|
}
|
return v2;
|
};
|
|
/**
|
* 变换局部坐标位置到全局坐标空间
|
* @method
|
* @param {number} x
|
* @param {number} y
|
* @return {Array.<number>}
|
*/
|
transformableProto.transformCoordToGlobal = function (x, y) {
|
var v2 = [x, y];
|
var transform = this.transform;
|
if (transform) {
|
vector.applyTransform(v2, v2, transform);
|
}
|
return v2;
|
};
|
|
/**
|
* @static
|
* @param {Object} target
|
* @param {Array.<number>} target.origin
|
* @param {number} target.rotation
|
* @param {Array.<number>} target.position
|
* @param {Array.<number>} [m]
|
*/
|
Transformable.getLocalTransform = function (target, m) {
|
m = m || [];
|
mIdentity(m);
|
|
var origin = target.origin;
|
var scale = target.scale || [1, 1];
|
var rotation = target.rotation || 0;
|
var position = target.position || [0, 0];
|
|
if (origin) {
|
// Translate to origin
|
m[4] -= origin[0];
|
m[5] -= origin[1];
|
}
|
matrix.scale(m, m, scale);
|
if (rotation) {
|
matrix.rotate(m, m, rotation);
|
}
|
if (origin) {
|
// Translate back from origin
|
m[4] += origin[0];
|
m[5] += origin[1];
|
}
|
|
m[4] += position[0];
|
m[5] += position[1];
|
|
return m;
|
};
|
|
module.exports = Transformable;
|
|
|
|
/***/ },
|
/* 35 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* @module zrender/mixin/Animatable
|
*/
|
|
|
var Animator = __webpack_require__(36);
|
var util = __webpack_require__(4);
|
var isString = util.isString;
|
var isFunction = util.isFunction;
|
var isObject = util.isObject;
|
var log = __webpack_require__(41);
|
|
/**
|
* @alias modue:zrender/mixin/Animatable
|
* @constructor
|
*/
|
var Animatable = function () {
|
|
/**
|
* @type {Array.<module:zrender/animation/Animator>}
|
* @readOnly
|
*/
|
this.animators = [];
|
};
|
|
Animatable.prototype = {
|
|
constructor: Animatable,
|
|
/**
|
* 动画
|
*
|
* @param {string} path 需要添加动画的属性获取路径,可以通过a.b.c来获取深层的属性
|
* @param {boolean} [loop] 动画是否循环
|
* @return {module:zrender/animation/Animator}
|
* @example:
|
* el.animate('style', false)
|
* .when(1000, {x: 10} )
|
* .done(function(){ // Animation done })
|
* .start()
|
*/
|
animate: function (path, loop) {
|
var target;
|
var animatingShape = false;
|
var el = this;
|
var zr = this.__zr;
|
if (path) {
|
var pathSplitted = path.split('.');
|
var prop = el;
|
// If animating shape
|
animatingShape = pathSplitted[0] === 'shape';
|
for (var i = 0, l = pathSplitted.length; i < l; i++) {
|
if (!prop) {
|
continue;
|
}
|
prop = prop[pathSplitted[i]];
|
}
|
if (prop) {
|
target = prop;
|
}
|
}
|
else {
|
target = el;
|
}
|
|
if (!target) {
|
log(
|
'Property "'
|
+ path
|
+ '" is not existed in element '
|
+ el.id
|
);
|
return;
|
}
|
|
var animators = el.animators;
|
|
var animator = new Animator(target, loop);
|
|
animator.during(function (target) {
|
el.dirty(animatingShape);
|
})
|
.done(function () {
|
// FIXME Animator will not be removed if use `Animator#stop` to stop animation
|
animators.splice(util.indexOf(animators, animator), 1);
|
});
|
|
animators.push(animator);
|
|
// If animate after added to the zrender
|
if (zr) {
|
zr.animation.addAnimator(animator);
|
}
|
|
return animator;
|
},
|
|
/**
|
* 停止动画
|
* @param {boolean} forwardToLast If move to last frame before stop
|
*/
|
stopAnimation: function (forwardToLast) {
|
var animators = this.animators;
|
var len = animators.length;
|
for (var i = 0; i < len; i++) {
|
animators[i].stop(forwardToLast);
|
}
|
animators.length = 0;
|
|
return this;
|
},
|
|
/**
|
* @param {Object} target
|
* @param {number} [time=500] Time in ms
|
* @param {string} [easing='linear']
|
* @param {number} [delay=0]
|
* @param {Function} [callback]
|
*
|
* @example
|
* // Animate position
|
* el.animateTo({
|
* position: [10, 10]
|
* }, function () { // done })
|
*
|
* // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing
|
* el.animateTo({
|
* shape: {
|
* width: 500
|
* },
|
* style: {
|
* fill: 'red'
|
* }
|
* position: [10, 10]
|
* }, 100, 100, 'cubicOut', function () { // done })
|
*/
|
// TODO Return animation key
|
animateTo: function (target, time, delay, easing, callback) {
|
// animateTo(target, time, easing, callback);
|
if (isString(delay)) {
|
callback = easing;
|
easing = delay;
|
delay = 0;
|
}
|
// animateTo(target, time, delay, callback);
|
else if (isFunction(easing)) {
|
callback = easing;
|
easing = 'linear';
|
delay = 0;
|
}
|
// animateTo(target, time, callback);
|
else if (isFunction(delay)) {
|
callback = delay;
|
delay = 0;
|
}
|
// animateTo(target, callback)
|
else if (isFunction(time)) {
|
callback = time;
|
time = 500;
|
}
|
// animateTo(target)
|
else if (!time) {
|
time = 500;
|
}
|
// Stop all previous animations
|
this.stopAnimation();
|
this._animateToShallow('', this, target, time, delay, easing, callback);
|
|
// Animators may be removed immediately after start
|
// if there is nothing to animate
|
var animators = this.animators.slice();
|
var count = animators.length;
|
function done() {
|
count--;
|
if (!count) {
|
callback && callback();
|
}
|
}
|
|
// No animators. This should be checked before animators[i].start(),
|
// because 'done' may be executed immediately if no need to animate.
|
if (!count) {
|
callback && callback();
|
}
|
// Start after all animators created
|
// Incase any animator is done immediately when all animation properties are not changed
|
for (var i = 0; i < animators.length; i++) {
|
animators[i]
|
.done(done)
|
.start(easing);
|
}
|
},
|
|
/**
|
* @private
|
* @param {string} path=''
|
* @param {Object} source=this
|
* @param {Object} target
|
* @param {number} [time=500]
|
* @param {number} [delay=0]
|
*
|
* @example
|
* // Animate position
|
* el._animateToShallow({
|
* position: [10, 10]
|
* })
|
*
|
* // Animate shape, style and position in 100ms, delayed 100ms
|
* el._animateToShallow({
|
* shape: {
|
* width: 500
|
* },
|
* style: {
|
* fill: 'red'
|
* }
|
* position: [10, 10]
|
* }, 100, 100)
|
*/
|
_animateToShallow: function (path, source, target, time, delay) {
|
var objShallow = {};
|
var propertyCount = 0;
|
for (var name in target) {
|
if (!target.hasOwnProperty(name)) {
|
continue;
|
}
|
|
if (source[name] != null) {
|
if (isObject(target[name]) && !util.isArrayLike(target[name])) {
|
this._animateToShallow(
|
path ? path + '.' + name : name,
|
source[name],
|
target[name],
|
time,
|
delay
|
);
|
}
|
else {
|
objShallow[name] = target[name];
|
propertyCount++;
|
}
|
}
|
else if (target[name] != null) {
|
// Attr directly if not has property
|
// FIXME, if some property not needed for element ?
|
if (!path) {
|
this.attr(name, target[name]);
|
}
|
else { // Shape or style
|
var props = {};
|
props[path] = {};
|
props[path][name] = target[name];
|
this.attr(props);
|
}
|
}
|
}
|
|
if (propertyCount > 0) {
|
this.animate(path, false)
|
.when(time == null ? 500 : time, objShallow)
|
.delay(delay || 0);
|
}
|
|
return this;
|
}
|
};
|
|
module.exports = Animatable;
|
|
|
/***/ },
|
/* 36 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/animation/Animator
|
*/
|
|
|
var Clip = __webpack_require__(37);
|
var color = __webpack_require__(39);
|
var util = __webpack_require__(4);
|
var isArrayLike = util.isArrayLike;
|
|
var arraySlice = Array.prototype.slice;
|
|
function defaultGetter(target, key) {
|
return target[key];
|
}
|
|
function defaultSetter(target, key, value) {
|
target[key] = value;
|
}
|
|
/**
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} percent
|
* @return {number}
|
*/
|
function interpolateNumber(p0, p1, percent) {
|
return (p1 - p0) * percent + p0;
|
}
|
|
/**
|
* @param {string} p0
|
* @param {string} p1
|
* @param {number} percent
|
* @return {string}
|
*/
|
function interpolateString(p0, p1, percent) {
|
return percent > 0.5 ? p1 : p0;
|
}
|
|
/**
|
* @param {Array} p0
|
* @param {Array} p1
|
* @param {number} percent
|
* @param {Array} out
|
* @param {number} arrDim
|
*/
|
function interpolateArray(p0, p1, percent, out, arrDim) {
|
var len = p0.length;
|
if (arrDim == 1) {
|
for (var i = 0; i < len; i++) {
|
out[i] = interpolateNumber(p0[i], p1[i], percent);
|
}
|
}
|
else {
|
var len2 = p0[0].length;
|
for (var i = 0; i < len; i++) {
|
for (var j = 0; j < len2; j++) {
|
out[i][j] = interpolateNumber(
|
p0[i][j], p1[i][j], percent
|
);
|
}
|
}
|
}
|
}
|
|
// arr0 is source array, arr1 is target array.
|
// Do some preprocess to avoid error happened when interpolating from arr0 to arr1
|
function fillArr(arr0, arr1, arrDim) {
|
var arr0Len = arr0.length;
|
var arr1Len = arr1.length;
|
if (arr0Len !== arr1Len) {
|
// FIXME Not work for TypedArray
|
var isPreviousLarger = arr0Len > arr1Len;
|
if (isPreviousLarger) {
|
// Cut the previous
|
arr0.length = arr1Len;
|
}
|
else {
|
// Fill the previous
|
for (var i = arr0Len; i < arr1Len; i++) {
|
arr0.push(
|
arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])
|
);
|
}
|
}
|
}
|
// Handling NaN value
|
var len2 = arr0[0] && arr0[0].length;
|
for (var i = 0; i < arr0.length; i++) {
|
if (arrDim === 1) {
|
if (isNaN(arr0[i])) {
|
arr0[i] = arr1[i];
|
}
|
}
|
else {
|
for (var j = 0; j < len2; j++) {
|
if (isNaN(arr0[i][j])) {
|
arr0[i][j] = arr1[i][j];
|
}
|
}
|
}
|
}
|
}
|
|
/**
|
* @param {Array} arr0
|
* @param {Array} arr1
|
* @param {number} arrDim
|
* @return {boolean}
|
*/
|
function isArraySame(arr0, arr1, arrDim) {
|
if (arr0 === arr1) {
|
return true;
|
}
|
var len = arr0.length;
|
if (len !== arr1.length) {
|
return false;
|
}
|
if (arrDim === 1) {
|
for (var i = 0; i < len; i++) {
|
if (arr0[i] !== arr1[i]) {
|
return false;
|
}
|
}
|
}
|
else {
|
var len2 = arr0[0].length;
|
for (var i = 0; i < len; i++) {
|
for (var j = 0; j < len2; j++) {
|
if (arr0[i][j] !== arr1[i][j]) {
|
return false;
|
}
|
}
|
}
|
}
|
return true;
|
}
|
|
/**
|
* Catmull Rom interpolate array
|
* @param {Array} p0
|
* @param {Array} p1
|
* @param {Array} p2
|
* @param {Array} p3
|
* @param {number} t
|
* @param {number} t2
|
* @param {number} t3
|
* @param {Array} out
|
* @param {number} arrDim
|
*/
|
function catmullRomInterpolateArray(
|
p0, p1, p2, p3, t, t2, t3, out, arrDim
|
) {
|
var len = p0.length;
|
if (arrDim == 1) {
|
for (var i = 0; i < len; i++) {
|
out[i] = catmullRomInterpolate(
|
p0[i], p1[i], p2[i], p3[i], t, t2, t3
|
);
|
}
|
}
|
else {
|
var len2 = p0[0].length;
|
for (var i = 0; i < len; i++) {
|
for (var j = 0; j < len2; j++) {
|
out[i][j] = catmullRomInterpolate(
|
p0[i][j], p1[i][j], p2[i][j], p3[i][j],
|
t, t2, t3
|
);
|
}
|
}
|
}
|
}
|
|
/**
|
* Catmull Rom interpolate number
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {number} t
|
* @param {number} t2
|
* @param {number} t3
|
* @return {number}
|
*/
|
function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
|
var v0 = (p2 - p0) * 0.5;
|
var v1 = (p3 - p1) * 0.5;
|
return (2 * (p1 - p2) + v0 + v1) * t3
|
+ (-3 * (p1 - p2) - 2 * v0 - v1) * t2
|
+ v0 * t + p1;
|
}
|
|
function cloneValue(value) {
|
if (isArrayLike(value)) {
|
var len = value.length;
|
if (isArrayLike(value[0])) {
|
var ret = [];
|
for (var i = 0; i < len; i++) {
|
ret.push(arraySlice.call(value[i]));
|
}
|
return ret;
|
}
|
|
return arraySlice.call(value);
|
}
|
|
return value;
|
}
|
|
function rgba2String(rgba) {
|
rgba[0] = Math.floor(rgba[0]);
|
rgba[1] = Math.floor(rgba[1]);
|
rgba[2] = Math.floor(rgba[2]);
|
|
return 'rgba(' + rgba.join(',') + ')';
|
}
|
|
function createTrackClip (animator, easing, oneTrackDone, keyframes, propName) {
|
var getter = animator._getter;
|
var setter = animator._setter;
|
var useSpline = easing === 'spline';
|
|
var trackLen = keyframes.length;
|
if (!trackLen) {
|
return;
|
}
|
// Guess data type
|
var firstVal = keyframes[0].value;
|
var isValueArray = isArrayLike(firstVal);
|
var isValueColor = false;
|
var isValueString = false;
|
|
// For vertices morphing
|
var arrDim = (
|
isValueArray
|
&& isArrayLike(firstVal[0])
|
)
|
? 2 : 1;
|
var trackMaxTime;
|
// Sort keyframe as ascending
|
keyframes.sort(function(a, b) {
|
return a.time - b.time;
|
});
|
|
trackMaxTime = keyframes[trackLen - 1].time;
|
// Percents of each keyframe
|
var kfPercents = [];
|
// Value of each keyframe
|
var kfValues = [];
|
var prevValue = keyframes[0].value;
|
var isAllValueEqual = true;
|
for (var i = 0; i < trackLen; i++) {
|
kfPercents.push(keyframes[i].time / trackMaxTime);
|
// Assume value is a color when it is a string
|
var value = keyframes[i].value;
|
|
// Check if value is equal, deep check if value is array
|
if (!((isValueArray && isArraySame(value, prevValue, arrDim))
|
|| (!isValueArray && value === prevValue))) {
|
isAllValueEqual = false;
|
}
|
prevValue = value;
|
|
// Try converting a string to a color array
|
if (typeof value == 'string') {
|
var colorArray = color.parse(value);
|
if (colorArray) {
|
value = colorArray;
|
isValueColor = true;
|
}
|
else {
|
isValueString = true;
|
}
|
}
|
kfValues.push(value);
|
}
|
if (isAllValueEqual) {
|
return;
|
}
|
|
var lastValue = kfValues[trackLen - 1];
|
// Polyfill array and NaN value
|
for (var i = 0; i < trackLen - 1; i++) {
|
if (isValueArray) {
|
fillArr(kfValues[i], lastValue, arrDim);
|
}
|
else {
|
if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) {
|
kfValues[i] = lastValue;
|
}
|
}
|
}
|
isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim);
|
|
// Cache the key of last frame to speed up when
|
// animation playback is sequency
|
var lastFrame = 0;
|
var lastFramePercent = 0;
|
var start;
|
var w;
|
var p0;
|
var p1;
|
var p2;
|
var p3;
|
|
if (isValueColor) {
|
var rgba = [0, 0, 0, 0];
|
}
|
|
var onframe = function (target, percent) {
|
// Find the range keyframes
|
// kf1-----kf2---------current--------kf3
|
// find kf2 and kf3 and do interpolation
|
var frame;
|
// In the easing function like elasticOut, percent may less than 0
|
if (percent < 0) {
|
frame = 0;
|
}
|
else if (percent < lastFramePercent) {
|
// Start from next key
|
// PENDING start from lastFrame ?
|
start = Math.min(lastFrame + 1, trackLen - 1);
|
for (frame = start; frame >= 0; frame--) {
|
if (kfPercents[frame] <= percent) {
|
break;
|
}
|
}
|
// PENDING really need to do this ?
|
frame = Math.min(frame, trackLen - 2);
|
}
|
else {
|
for (frame = lastFrame; frame < trackLen; frame++) {
|
if (kfPercents[frame] > percent) {
|
break;
|
}
|
}
|
frame = Math.min(frame - 1, trackLen - 2);
|
}
|
lastFrame = frame;
|
lastFramePercent = percent;
|
|
var range = (kfPercents[frame + 1] - kfPercents[frame]);
|
if (range === 0) {
|
return;
|
}
|
else {
|
w = (percent - kfPercents[frame]) / range;
|
}
|
if (useSpline) {
|
p1 = kfValues[frame];
|
p0 = kfValues[frame === 0 ? frame : frame - 1];
|
p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1];
|
p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2];
|
if (isValueArray) {
|
catmullRomInterpolateArray(
|
p0, p1, p2, p3, w, w * w, w * w * w,
|
getter(target, propName),
|
arrDim
|
);
|
}
|
else {
|
var value;
|
if (isValueColor) {
|
value = catmullRomInterpolateArray(
|
p0, p1, p2, p3, w, w * w, w * w * w,
|
rgba, 1
|
);
|
value = rgba2String(rgba);
|
}
|
else if (isValueString) {
|
// String is step(0.5)
|
return interpolateString(p1, p2, w);
|
}
|
else {
|
value = catmullRomInterpolate(
|
p0, p1, p2, p3, w, w * w, w * w * w
|
);
|
}
|
setter(
|
target,
|
propName,
|
value
|
);
|
}
|
}
|
else {
|
if (isValueArray) {
|
interpolateArray(
|
kfValues[frame], kfValues[frame + 1], w,
|
getter(target, propName),
|
arrDim
|
);
|
}
|
else {
|
var value;
|
if (isValueColor) {
|
interpolateArray(
|
kfValues[frame], kfValues[frame + 1], w,
|
rgba, 1
|
);
|
value = rgba2String(rgba);
|
}
|
else if (isValueString) {
|
// String is step(0.5)
|
return interpolateString(kfValues[frame], kfValues[frame + 1], w);
|
}
|
else {
|
value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w);
|
}
|
setter(
|
target,
|
propName,
|
value
|
);
|
}
|
}
|
};
|
|
var clip = new Clip({
|
target: animator._target,
|
life: trackMaxTime,
|
loop: animator._loop,
|
delay: animator._delay,
|
onframe: onframe,
|
ondestroy: oneTrackDone
|
});
|
|
if (easing && easing !== 'spline') {
|
clip.easing = easing;
|
}
|
|
return clip;
|
}
|
|
/**
|
* @alias module:zrender/animation/Animator
|
* @constructor
|
* @param {Object} target
|
* @param {boolean} loop
|
* @param {Function} getter
|
* @param {Function} setter
|
*/
|
var Animator = function(target, loop, getter, setter) {
|
this._tracks = {};
|
this._target = target;
|
|
this._loop = loop || false;
|
|
this._getter = getter || defaultGetter;
|
this._setter = setter || defaultSetter;
|
|
this._clipCount = 0;
|
|
this._delay = 0;
|
|
this._doneList = [];
|
|
this._onframeList = [];
|
|
this._clipList = [];
|
};
|
|
Animator.prototype = {
|
/**
|
* 设置动画关键帧
|
* @param {number} time 关键帧时间,单位是ms
|
* @param {Object} props 关键帧的属性值,key-value表示
|
* @return {module:zrender/animation/Animator}
|
*/
|
when: function(time /* ms */, props) {
|
var tracks = this._tracks;
|
for (var propName in props) {
|
if (!props.hasOwnProperty(propName)) {
|
continue;
|
}
|
|
if (!tracks[propName]) {
|
tracks[propName] = [];
|
// Invalid value
|
var value = this._getter(this._target, propName);
|
if (value == null) {
|
// zrLog('Invalid property ' + propName);
|
continue;
|
}
|
// If time is 0
|
// Then props is given initialize value
|
// Else
|
// Initialize value from current prop value
|
if (time !== 0) {
|
tracks[propName].push({
|
time: 0,
|
value: cloneValue(value)
|
});
|
}
|
}
|
tracks[propName].push({
|
time: time,
|
value: props[propName]
|
});
|
}
|
return this;
|
},
|
/**
|
* 添加动画每一帧的回调函数
|
* @param {Function} callback
|
* @return {module:zrender/animation/Animator}
|
*/
|
during: function (callback) {
|
this._onframeList.push(callback);
|
return this;
|
},
|
|
pause: function () {
|
for (var i = 0; i < this._clipList.length; i++) {
|
this._clipList[i].pause();
|
}
|
this._paused = true;
|
},
|
|
resume: function () {
|
for (var i = 0; i < this._clipList.length; i++) {
|
this._clipList[i].resume();
|
}
|
this._paused = false;
|
},
|
|
isPaused: function () {
|
return !!this._paused;
|
},
|
|
_doneCallback: function () {
|
// Clear all tracks
|
this._tracks = {};
|
// Clear all clips
|
this._clipList.length = 0;
|
|
var doneList = this._doneList;
|
var len = doneList.length;
|
for (var i = 0; i < len; i++) {
|
doneList[i].call(this);
|
}
|
},
|
/**
|
* 开始执行动画
|
* @param {string|Function} easing
|
* 动画缓动函数,详见{@link module:zrender/animation/easing}
|
* @return {module:zrender/animation/Animator}
|
*/
|
start: function (easing) {
|
|
var self = this;
|
var clipCount = 0;
|
|
var oneTrackDone = function() {
|
clipCount--;
|
if (!clipCount) {
|
self._doneCallback();
|
}
|
};
|
|
var lastClip;
|
for (var propName in this._tracks) {
|
if (!this._tracks.hasOwnProperty(propName)) {
|
continue;
|
}
|
var clip = createTrackClip(
|
this, easing, oneTrackDone,
|
this._tracks[propName], propName
|
);
|
if (clip) {
|
this._clipList.push(clip);
|
clipCount++;
|
|
// If start after added to animation
|
if (this.animation) {
|
this.animation.addClip(clip);
|
}
|
|
lastClip = clip;
|
}
|
}
|
|
// Add during callback on the last clip
|
if (lastClip) {
|
var oldOnFrame = lastClip.onframe;
|
lastClip.onframe = function (target, percent) {
|
oldOnFrame(target, percent);
|
|
for (var i = 0; i < self._onframeList.length; i++) {
|
self._onframeList[i](target, percent);
|
}
|
};
|
}
|
|
if (!clipCount) {
|
this._doneCallback();
|
}
|
return this;
|
},
|
/**
|
* 停止动画
|
* @param {boolean} forwardToLast If move to last frame before stop
|
*/
|
stop: function (forwardToLast) {
|
var clipList = this._clipList;
|
var animation = this.animation;
|
for (var i = 0; i < clipList.length; i++) {
|
var clip = clipList[i];
|
if (forwardToLast) {
|
// Move to last frame before stop
|
clip.onframe(this._target, 1);
|
}
|
animation && animation.removeClip(clip);
|
}
|
clipList.length = 0;
|
},
|
/**
|
* 设置动画延迟开始的时间
|
* @param {number} time 单位ms
|
* @return {module:zrender/animation/Animator}
|
*/
|
delay: function (time) {
|
this._delay = time;
|
return this;
|
},
|
/**
|
* 添加动画结束的回调
|
* @param {Function} cb
|
* @return {module:zrender/animation/Animator}
|
*/
|
done: function(cb) {
|
if (cb) {
|
this._doneList.push(cb);
|
}
|
return this;
|
},
|
|
/**
|
* @return {Array.<module:zrender/animation/Clip>}
|
*/
|
getClips: function () {
|
return this._clipList;
|
}
|
};
|
|
module.exports = Animator;
|
|
|
/***/ },
|
/* 37 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 动画主控制器
|
* @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
|
* @config life(1000) 动画时长
|
* @config delay(0) 动画延迟时间
|
* @config loop(true)
|
* @config gap(0) 循环的间隔时间
|
* @config onframe
|
* @config easing(optional)
|
* @config ondestroy(optional)
|
* @config onrestart(optional)
|
*
|
* TODO pause
|
*/
|
|
|
var easingFuncs = __webpack_require__(38);
|
|
function Clip(options) {
|
|
this._target = options.target;
|
|
// 生命周期
|
this._life = options.life || 1000;
|
// 延时
|
this._delay = options.delay || 0;
|
// 开始时间
|
// this._startTime = new Date().getTime() + this._delay;// 单位毫秒
|
this._initialized = false;
|
|
// 是否循环
|
this.loop = options.loop == null ? false : options.loop;
|
|
this.gap = options.gap || 0;
|
|
this.easing = options.easing || 'Linear';
|
|
this.onframe = options.onframe;
|
this.ondestroy = options.ondestroy;
|
this.onrestart = options.onrestart;
|
|
this._pausedTime = 0;
|
this._paused = false;
|
}
|
|
Clip.prototype = {
|
|
constructor: Clip,
|
|
step: function (globalTime, deltaTime) {
|
// Set startTime on first step, or _startTime may has milleseconds different between clips
|
// PENDING
|
if (!this._initialized) {
|
this._startTime = globalTime + this._delay;
|
this._initialized = true;
|
}
|
|
if (this._paused) {
|
this._pausedTime += deltaTime;
|
return;
|
}
|
|
var percent = (globalTime - this._startTime - this._pausedTime) / this._life;
|
|
// 还没开始
|
if (percent < 0) {
|
return;
|
}
|
|
percent = Math.min(percent, 1);
|
|
var easing = this.easing;
|
var easingFunc = typeof easing == 'string' ? easingFuncs[easing] : easing;
|
var schedule = typeof easingFunc === 'function'
|
? easingFunc(percent)
|
: percent;
|
|
this.fire('frame', schedule);
|
|
// 结束
|
if (percent == 1) {
|
if (this.loop) {
|
this.restart (globalTime);
|
// 重新开始周期
|
// 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件
|
return 'restart';
|
}
|
|
// 动画完成将这个控制器标识为待删除
|
// 在Animation.update中进行批量删除
|
this._needsRemove = true;
|
return 'destroy';
|
}
|
|
return null;
|
},
|
|
restart: function (globalTime) {
|
var remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
|
this._startTime = globalTime - remainder + this.gap;
|
this._pausedTime = 0;
|
|
this._needsRemove = false;
|
},
|
|
fire: function (eventType, arg) {
|
eventType = 'on' + eventType;
|
if (this[eventType]) {
|
this[eventType](this._target, arg);
|
}
|
},
|
|
pause: function () {
|
this._paused = true;
|
},
|
|
resume: function () {
|
this._paused = false;
|
}
|
};
|
|
module.exports = Clip;
|
|
|
|
/***/ },
|
/* 38 */
|
/***/ function(module, exports) {
|
|
/**
|
* 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js
|
* @see http://sole.github.io/tween.js/examples/03_graphs.html
|
* @exports zrender/animation/easing
|
*/
|
|
var easing = {
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
linear: function (k) {
|
return k;
|
},
|
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quadraticIn: function (k) {
|
return k * k;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quadraticOut: function (k) {
|
return k * (2 - k);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quadraticInOut: function (k) {
|
if ((k *= 2) < 1) {
|
return 0.5 * k * k;
|
}
|
return -0.5 * (--k * (k - 2) - 1);
|
},
|
|
// 三次方的缓动(t^3)
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
cubicIn: function (k) {
|
return k * k * k;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
cubicOut: function (k) {
|
return --k * k * k + 1;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
cubicInOut: function (k) {
|
if ((k *= 2) < 1) {
|
return 0.5 * k * k * k;
|
}
|
return 0.5 * ((k -= 2) * k * k + 2);
|
},
|
|
// 四次方的缓动(t^4)
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quarticIn: function (k) {
|
return k * k * k * k;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quarticOut: function (k) {
|
return 1 - (--k * k * k * k);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quarticInOut: function (k) {
|
if ((k *= 2) < 1) {
|
return 0.5 * k * k * k * k;
|
}
|
return -0.5 * ((k -= 2) * k * k * k - 2);
|
},
|
|
// 五次方的缓动(t^5)
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quinticIn: function (k) {
|
return k * k * k * k * k;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quinticOut: function (k) {
|
return --k * k * k * k * k + 1;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
quinticInOut: function (k) {
|
if ((k *= 2) < 1) {
|
return 0.5 * k * k * k * k * k;
|
}
|
return 0.5 * ((k -= 2) * k * k * k * k + 2);
|
},
|
|
// 正弦曲线的缓动(sin(t))
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
sinusoidalIn: function (k) {
|
return 1 - Math.cos(k * Math.PI / 2);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
sinusoidalOut: function (k) {
|
return Math.sin(k * Math.PI / 2);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
sinusoidalInOut: function (k) {
|
return 0.5 * (1 - Math.cos(Math.PI * k));
|
},
|
|
// 指数曲线的缓动(2^t)
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
exponentialIn: function (k) {
|
return k === 0 ? 0 : Math.pow(1024, k - 1);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
exponentialOut: function (k) {
|
return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
exponentialInOut: function (k) {
|
if (k === 0) {
|
return 0;
|
}
|
if (k === 1) {
|
return 1;
|
}
|
if ((k *= 2) < 1) {
|
return 0.5 * Math.pow(1024, k - 1);
|
}
|
return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
|
},
|
|
// 圆形曲线的缓动(sqrt(1-t^2))
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
circularIn: function (k) {
|
return 1 - Math.sqrt(1 - k * k);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
circularOut: function (k) {
|
return Math.sqrt(1 - (--k * k));
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
circularInOut: function (k) {
|
if ((k *= 2) < 1) {
|
return -0.5 * (Math.sqrt(1 - k * k) - 1);
|
}
|
return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
|
},
|
|
// 创建类似于弹簧在停止前来回振荡的动画
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
elasticIn: function (k) {
|
var s;
|
var a = 0.1;
|
var p = 0.4;
|
if (k === 0) {
|
return 0;
|
}
|
if (k === 1) {
|
return 1;
|
}
|
if (!a || a < 1) {
|
a = 1; s = p / 4;
|
}
|
else {
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
}
|
return -(a * Math.pow(2, 10 * (k -= 1)) *
|
Math.sin((k - s) * (2 * Math.PI) / p));
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
elasticOut: function (k) {
|
var s;
|
var a = 0.1;
|
var p = 0.4;
|
if (k === 0) {
|
return 0;
|
}
|
if (k === 1) {
|
return 1;
|
}
|
if (!a || a < 1) {
|
a = 1; s = p / 4;
|
}
|
else {
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
}
|
return (a * Math.pow(2, -10 * k) *
|
Math.sin((k - s) * (2 * Math.PI) / p) + 1);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
elasticInOut: function (k) {
|
var s;
|
var a = 0.1;
|
var p = 0.4;
|
if (k === 0) {
|
return 0;
|
}
|
if (k === 1) {
|
return 1;
|
}
|
if (!a || a < 1) {
|
a = 1; s = p / 4;
|
}
|
else {
|
s = p * Math.asin(1 / a) / (2 * Math.PI);
|
}
|
if ((k *= 2) < 1) {
|
return -0.5 * (a * Math.pow(2, 10 * (k -= 1))
|
* Math.sin((k - s) * (2 * Math.PI) / p));
|
}
|
return a * Math.pow(2, -10 * (k -= 1))
|
* Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
|
},
|
|
// 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
backIn: function (k) {
|
var s = 1.70158;
|
return k * k * ((s + 1) * k - s);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
backOut: function (k) {
|
var s = 1.70158;
|
return --k * k * ((s + 1) * k + s) + 1;
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
backInOut: function (k) {
|
var s = 1.70158 * 1.525;
|
if ((k *= 2) < 1) {
|
return 0.5 * (k * k * ((s + 1) * k - s));
|
}
|
return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
|
},
|
|
// 创建弹跳效果
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
bounceIn: function (k) {
|
return 1 - easing.bounceOut(1 - k);
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
bounceOut: function (k) {
|
if (k < (1 / 2.75)) {
|
return 7.5625 * k * k;
|
}
|
else if (k < (2 / 2.75)) {
|
return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
|
}
|
else if (k < (2.5 / 2.75)) {
|
return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
|
}
|
else {
|
return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
|
}
|
},
|
/**
|
* @param {number} k
|
* @return {number}
|
*/
|
bounceInOut: function (k) {
|
if (k < 0.5) {
|
return easing.bounceIn(k * 2) * 0.5;
|
}
|
return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5;
|
}
|
};
|
|
module.exports = easing;
|
|
|
|
|
/***/ },
|
/* 39 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module zrender/tool/color
|
*/
|
|
|
var LRU = __webpack_require__(40);
|
|
var kCSSColorTable = {
|
'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1],
|
'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1],
|
'aquamarine': [127,255,212,1], 'azure': [240,255,255,1],
|
'beige': [245,245,220,1], 'bisque': [255,228,196,1],
|
'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1],
|
'blue': [0,0,255,1], 'blueviolet': [138,43,226,1],
|
'brown': [165,42,42,1], 'burlywood': [222,184,135,1],
|
'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1],
|
'chocolate': [210,105,30,1], 'coral': [255,127,80,1],
|
'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1],
|
'crimson': [220,20,60,1], 'cyan': [0,255,255,1],
|
'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1],
|
'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1],
|
'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1],
|
'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1],
|
'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1],
|
'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1],
|
'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1],
|
'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1],
|
'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1],
|
'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1],
|
'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1],
|
'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1],
|
'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1],
|
'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1],
|
'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1],
|
'gold': [255,215,0,1], 'goldenrod': [218,165,32,1],
|
'gray': [128,128,128,1], 'green': [0,128,0,1],
|
'greenyellow': [173,255,47,1], 'grey': [128,128,128,1],
|
'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1],
|
'indianred': [205,92,92,1], 'indigo': [75,0,130,1],
|
'ivory': [255,255,240,1], 'khaki': [240,230,140,1],
|
'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1],
|
'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1],
|
'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1],
|
'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1],
|
'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1],
|
'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1],
|
'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1],
|
'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1],
|
'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1],
|
'lightyellow': [255,255,224,1], 'lime': [0,255,0,1],
|
'limegreen': [50,205,50,1], 'linen': [250,240,230,1],
|
'magenta': [255,0,255,1], 'maroon': [128,0,0,1],
|
'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1],
|
'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1],
|
'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1],
|
'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1],
|
'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1],
|
'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1],
|
'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1],
|
'navy': [0,0,128,1], 'oldlace': [253,245,230,1],
|
'olive': [128,128,0,1], 'olivedrab': [107,142,35,1],
|
'orange': [255,165,0,1], 'orangered': [255,69,0,1],
|
'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1],
|
'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1],
|
'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1],
|
'peachpuff': [255,218,185,1], 'peru': [205,133,63,1],
|
'pink': [255,192,203,1], 'plum': [221,160,221,1],
|
'powderblue': [176,224,230,1], 'purple': [128,0,128,1],
|
'red': [255,0,0,1], 'rosybrown': [188,143,143,1],
|
'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1],
|
'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1],
|
'seagreen': [46,139,87,1], 'seashell': [255,245,238,1],
|
'sienna': [160,82,45,1], 'silver': [192,192,192,1],
|
'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1],
|
'slategray': [112,128,144,1], 'slategrey': [112,128,144,1],
|
'snow': [255,250,250,1], 'springgreen': [0,255,127,1],
|
'steelblue': [70,130,180,1], 'tan': [210,180,140,1],
|
'teal': [0,128,128,1], 'thistle': [216,191,216,1],
|
'tomato': [255,99,71,1], 'turquoise': [64,224,208,1],
|
'violet': [238,130,238,1], 'wheat': [245,222,179,1],
|
'white': [255,255,255,1], 'whitesmoke': [245,245,245,1],
|
'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1]
|
};
|
|
function clampCssByte(i) { // Clamp to integer 0 .. 255.
|
i = Math.round(i); // Seems to be what Chrome does (vs truncation).
|
return i < 0 ? 0 : i > 255 ? 255 : i;
|
}
|
|
function clampCssAngle(i) { // Clamp to integer 0 .. 360.
|
i = Math.round(i); // Seems to be what Chrome does (vs truncation).
|
return i < 0 ? 0 : i > 360 ? 360 : i;
|
}
|
|
function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0.
|
return f < 0 ? 0 : f > 1 ? 1 : f;
|
}
|
|
function parseCssInt(str) { // int or percentage.
|
if (str.length && str.charAt(str.length - 1) === '%') {
|
return clampCssByte(parseFloat(str) / 100 * 255);
|
}
|
return clampCssByte(parseInt(str, 10));
|
}
|
|
function parseCssFloat(str) { // float or percentage.
|
if (str.length && str.charAt(str.length - 1) === '%') {
|
return clampCssFloat(parseFloat(str) / 100);
|
}
|
return clampCssFloat(parseFloat(str));
|
}
|
|
function cssHueToRgb(m1, m2, h) {
|
if (h < 0) {
|
h += 1;
|
}
|
else if (h > 1) {
|
h -= 1;
|
}
|
|
if (h * 6 < 1) {
|
return m1 + (m2 - m1) * h * 6;
|
}
|
if (h * 2 < 1) {
|
return m2;
|
}
|
if (h * 3 < 2) {
|
return m1 + (m2 - m1) * (2/3 - h) * 6;
|
}
|
return m1;
|
}
|
|
function lerp(a, b, p) {
|
return a + (b - a) * p;
|
}
|
|
function setRgba(out, r, g, b, a) {
|
out[0] = r; out[1] = g; out[2] = b; out[3] = a;
|
return out;
|
}
|
function copyRgba(out, a) {
|
out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3];
|
return out;
|
}
|
var colorCache = new LRU(20);
|
var lastRemovedArr = null;
|
function putToCache(colorStr, rgbaArr) {
|
// Reuse removed array
|
if (lastRemovedArr) {
|
copyRgba(lastRemovedArr, rgbaArr);
|
}
|
lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
|
}
|
/**
|
* @param {string} colorStr
|
* @param {Array.<number>} out
|
* @return {Array.<number>}
|
* @memberOf module:zrender/util/color
|
*/
|
function parse(colorStr, rgbaArr) {
|
if (!colorStr) {
|
return;
|
}
|
rgbaArr = rgbaArr || [];
|
|
var cached = colorCache.get(colorStr);
|
if (cached) {
|
return copyRgba(rgbaArr, cached);
|
}
|
|
// colorStr may be not string
|
colorStr = colorStr + '';
|
// Remove all whitespace, not compliant, but should just be more accepting.
|
var str = colorStr.replace(/ /g, '').toLowerCase();
|
|
// Color keywords (and transparent) lookup.
|
if (str in kCSSColorTable) {
|
copyRgba(rgbaArr, kCSSColorTable[str]);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
}
|
|
// #abc and #abc123 syntax.
|
if (str.charAt(0) === '#') {
|
if (str.length === 4) {
|
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
|
if (!(iv >= 0 && iv <= 0xfff)) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return; // Covers NaN.
|
}
|
setRgba(rgbaArr,
|
((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
|
(iv & 0xf0) | ((iv & 0xf0) >> 4),
|
(iv & 0xf) | ((iv & 0xf) << 4),
|
1
|
);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
}
|
else if (str.length === 7) {
|
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
|
if (!(iv >= 0 && iv <= 0xffffff)) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return; // Covers NaN.
|
}
|
setRgba(rgbaArr,
|
(iv & 0xff0000) >> 16,
|
(iv & 0xff00) >> 8,
|
iv & 0xff,
|
1
|
);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
}
|
|
return;
|
}
|
var op = str.indexOf('('), ep = str.indexOf(')');
|
if (op !== -1 && ep + 1 === str.length) {
|
var fname = str.substr(0, op);
|
var params = str.substr(op + 1, ep - (op + 1)).split(',');
|
var alpha = 1; // To allow case fallthrough.
|
switch (fname) {
|
case 'rgba':
|
if (params.length !== 4) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return;
|
}
|
alpha = parseCssFloat(params.pop()); // jshint ignore:line
|
// Fall through.
|
case 'rgb':
|
if (params.length !== 3) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return;
|
}
|
setRgba(rgbaArr,
|
parseCssInt(params[0]),
|
parseCssInt(params[1]),
|
parseCssInt(params[2]),
|
alpha
|
);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
case 'hsla':
|
if (params.length !== 4) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return;
|
}
|
params[3] = parseCssFloat(params[3]);
|
hsla2rgba(params, rgbaArr);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
case 'hsl':
|
if (params.length !== 3) {
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return;
|
}
|
hsla2rgba(params, rgbaArr);
|
putToCache(colorStr, rgbaArr);
|
return rgbaArr;
|
default:
|
return;
|
}
|
}
|
|
setRgba(rgbaArr, 0, 0, 0, 1);
|
return;
|
}
|
|
/**
|
* @param {Array.<number>} hsla
|
* @param {Array.<number>} rgba
|
* @return {Array.<number>} rgba
|
*/
|
function hsla2rgba(hsla, rgba) {
|
var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1
|
// NOTE(deanm): According to the CSS spec s/l should only be
|
// percentages, but we don't bother and let float or percentage.
|
var s = parseCssFloat(hsla[1]);
|
var l = parseCssFloat(hsla[2]);
|
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
|
var m1 = l * 2 - m2;
|
|
rgba = rgba || [];
|
setRgba(rgba,
|
clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
|
clampCssByte(cssHueToRgb(m1, m2, h) * 255),
|
clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
|
1
|
);
|
|
if (hsla.length === 4) {
|
rgba[3] = hsla[3];
|
}
|
|
return rgba;
|
}
|
|
/**
|
* @param {Array.<number>} rgba
|
* @return {Array.<number>} hsla
|
*/
|
function rgba2hsla(rgba) {
|
if (!rgba) {
|
return;
|
}
|
|
// RGB from 0 to 255
|
var R = rgba[0] / 255;
|
var G = rgba[1] / 255;
|
var B = rgba[2] / 255;
|
|
var vMin = Math.min(R, G, B); // Min. value of RGB
|
var vMax = Math.max(R, G, B); // Max. value of RGB
|
var delta = vMax - vMin; // Delta RGB value
|
|
var L = (vMax + vMin) / 2;
|
var H;
|
var S;
|
// HSL results from 0 to 1
|
if (delta === 0) {
|
H = 0;
|
S = 0;
|
}
|
else {
|
if (L < 0.5) {
|
S = delta / (vMax + vMin);
|
}
|
else {
|
S = delta / (2 - vMax - vMin);
|
}
|
|
var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;
|
var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;
|
var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;
|
|
if (R === vMax) {
|
H = deltaB - deltaG;
|
}
|
else if (G === vMax) {
|
H = (1 / 3) + deltaR - deltaB;
|
}
|
else if (B === vMax) {
|
H = (2 / 3) + deltaG - deltaR;
|
}
|
|
if (H < 0) {
|
H += 1;
|
}
|
|
if (H > 1) {
|
H -= 1;
|
}
|
}
|
|
var hsla = [H * 360, S, L];
|
|
if (rgba[3] != null) {
|
hsla.push(rgba[3]);
|
}
|
|
return hsla;
|
}
|
|
/**
|
* @param {string} color
|
* @param {number} level
|
* @return {string}
|
* @memberOf module:zrender/util/color
|
*/
|
function lift(color, level) {
|
var colorArr = parse(color);
|
if (colorArr) {
|
for (var i = 0; i < 3; i++) {
|
if (level < 0) {
|
colorArr[i] = colorArr[i] * (1 - level) | 0;
|
}
|
else {
|
colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
|
}
|
}
|
return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
|
}
|
}
|
|
/**
|
* @param {string} color
|
* @return {string}
|
* @memberOf module:zrender/util/color
|
*/
|
function toHex(color, level) {
|
var colorArr = parse(color);
|
if (colorArr) {
|
return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);
|
}
|
}
|
|
/**
|
* Map value to color. Faster than mapToColor methods because color is represented by rgba array.
|
* @param {number} normalizedValue A float between 0 and 1.
|
* @param {Array.<Array.<number>>} colors List of rgba color array
|
* @param {Array.<number>} [out] Mapped gba color array
|
* @return {Array.<number>} will be null/undefined if input illegal.
|
*/
|
function fastMapToColor(normalizedValue, colors, out) {
|
if (!(colors && colors.length)
|
|| !(normalizedValue >= 0 && normalizedValue <= 1)
|
) {
|
return;
|
}
|
|
out = out || [];
|
|
var value = normalizedValue * (colors.length - 1);
|
var leftIndex = Math.floor(value);
|
var rightIndex = Math.ceil(value);
|
var leftColor = colors[leftIndex];
|
var rightColor = colors[rightIndex];
|
var dv = value - leftIndex;
|
out[0] = clampCssByte(lerp(leftColor[0], rightColor[0], dv));
|
out[1] = clampCssByte(lerp(leftColor[1], rightColor[1], dv));
|
out[2] = clampCssByte(lerp(leftColor[2], rightColor[2], dv));
|
out[3] = clampCssFloat(lerp(leftColor[3], rightColor[3], dv));
|
|
return out;
|
}
|
/**
|
* @param {number} normalizedValue A float between 0 and 1.
|
* @param {Array.<string>} colors Color list.
|
* @param {boolean=} fullOutput Default false.
|
* @return {(string|Object)} Result color. If fullOutput,
|
* return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
|
* @memberOf module:zrender/util/color
|
*/
|
function mapToColor(normalizedValue, colors, fullOutput) {
|
if (!(colors && colors.length)
|
|| !(normalizedValue >= 0 && normalizedValue <= 1)
|
) {
|
return;
|
}
|
|
var value = normalizedValue * (colors.length - 1);
|
var leftIndex = Math.floor(value);
|
var rightIndex = Math.ceil(value);
|
var leftColor = parse(colors[leftIndex]);
|
var rightColor = parse(colors[rightIndex]);
|
var dv = value - leftIndex;
|
|
var color = stringify(
|
[
|
clampCssByte(lerp(leftColor[0], rightColor[0], dv)),
|
clampCssByte(lerp(leftColor[1], rightColor[1], dv)),
|
clampCssByte(lerp(leftColor[2], rightColor[2], dv)),
|
clampCssFloat(lerp(leftColor[3], rightColor[3], dv))
|
],
|
'rgba'
|
);
|
|
return fullOutput
|
? {
|
color: color,
|
leftIndex: leftIndex,
|
rightIndex: rightIndex,
|
value: value
|
}
|
: color;
|
}
|
|
/**
|
* @param {string} color
|
* @param {number=} h 0 ~ 360, ignore when null.
|
* @param {number=} s 0 ~ 1, ignore when null.
|
* @param {number=} l 0 ~ 1, ignore when null.
|
* @return {string} Color string in rgba format.
|
* @memberOf module:zrender/util/color
|
*/
|
function modifyHSL(color, h, s, l) {
|
color = parse(color);
|
|
if (color) {
|
color = rgba2hsla(color);
|
h != null && (color[0] = clampCssAngle(h));
|
s != null && (color[1] = parseCssFloat(s));
|
l != null && (color[2] = parseCssFloat(l));
|
|
return stringify(hsla2rgba(color), 'rgba');
|
}
|
}
|
|
/**
|
* @param {string} color
|
* @param {number=} alpha 0 ~ 1
|
* @return {string} Color string in rgba format.
|
* @memberOf module:zrender/util/color
|
*/
|
function modifyAlpha(color, alpha) {
|
color = parse(color);
|
|
if (color && alpha != null) {
|
color[3] = clampCssFloat(alpha);
|
return stringify(color, 'rgba');
|
}
|
}
|
|
/**
|
* @param {Array.<number>} arrColor like [12,33,44,0.4]
|
* @param {string} type 'rgba', 'hsva', ...
|
* @return {string} Result color. (If input illegal, return undefined).
|
*/
|
function stringify(arrColor, type) {
|
if (!arrColor || !arrColor.length) {
|
return;
|
}
|
var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
|
if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
|
colorStr += ',' + arrColor[3];
|
}
|
return type + '(' + colorStr + ')';
|
}
|
|
module.exports = {
|
parse: parse,
|
lift: lift,
|
toHex: toHex,
|
fastMapToColor: fastMapToColor,
|
mapToColor: mapToColor,
|
modifyHSL: modifyHSL,
|
modifyAlpha: modifyAlpha,
|
stringify: stringify
|
};
|
|
|
|
|
/***/ },
|
/* 40 */
|
/***/ function(module, exports) {
|
|
// Simple LRU cache use doubly linked list
|
// @module zrender/core/LRU
|
|
|
/**
|
* Simple double linked list. Compared with array, it has O(1) remove operation.
|
* @constructor
|
*/
|
var LinkedList = function () {
|
|
/**
|
* @type {module:zrender/core/LRU~Entry}
|
*/
|
this.head = null;
|
|
/**
|
* @type {module:zrender/core/LRU~Entry}
|
*/
|
this.tail = null;
|
|
this._len = 0;
|
};
|
|
var linkedListProto = LinkedList.prototype;
|
/**
|
* Insert a new value at the tail
|
* @param {} val
|
* @return {module:zrender/core/LRU~Entry}
|
*/
|
linkedListProto.insert = function (val) {
|
var entry = new Entry(val);
|
this.insertEntry(entry);
|
return entry;
|
};
|
|
/**
|
* Insert an entry at the tail
|
* @param {module:zrender/core/LRU~Entry} entry
|
*/
|
linkedListProto.insertEntry = function (entry) {
|
if (!this.head) {
|
this.head = this.tail = entry;
|
}
|
else {
|
this.tail.next = entry;
|
entry.prev = this.tail;
|
entry.next = null;
|
this.tail = entry;
|
}
|
this._len++;
|
};
|
|
/**
|
* Remove entry.
|
* @param {module:zrender/core/LRU~Entry} entry
|
*/
|
linkedListProto.remove = function (entry) {
|
var prev = entry.prev;
|
var next = entry.next;
|
if (prev) {
|
prev.next = next;
|
}
|
else {
|
// Is head
|
this.head = next;
|
}
|
if (next) {
|
next.prev = prev;
|
}
|
else {
|
// Is tail
|
this.tail = prev;
|
}
|
entry.next = entry.prev = null;
|
this._len--;
|
};
|
|
/**
|
* @return {number}
|
*/
|
linkedListProto.len = function () {
|
return this._len;
|
};
|
|
/**
|
* Clear list
|
*/
|
linkedListProto.clear = function () {
|
this.head = this.tail = null;
|
this._len = 0;
|
};
|
|
/**
|
* @constructor
|
* @param {} val
|
*/
|
var Entry = function (val) {
|
/**
|
* @type {}
|
*/
|
this.value = val;
|
|
/**
|
* @type {module:zrender/core/LRU~Entry}
|
*/
|
this.next;
|
|
/**
|
* @type {module:zrender/core/LRU~Entry}
|
*/
|
this.prev;
|
};
|
|
/**
|
* LRU Cache
|
* @constructor
|
* @alias module:zrender/core/LRU
|
*/
|
var LRU = function (maxSize) {
|
|
this._list = new LinkedList();
|
|
this._map = {};
|
|
this._maxSize = maxSize || 10;
|
|
this._lastRemovedEntry = null;
|
};
|
|
var LRUProto = LRU.prototype;
|
|
/**
|
* @param {string} key
|
* @param {} value
|
* @return {} Removed value
|
*/
|
LRUProto.put = function (key, value) {
|
var list = this._list;
|
var map = this._map;
|
var removed = null;
|
if (map[key] == null) {
|
var len = list.len();
|
// Reuse last removed entry
|
var entry = this._lastRemovedEntry;
|
|
if (len >= this._maxSize && len > 0) {
|
// Remove the least recently used
|
var leastUsedEntry = list.head;
|
list.remove(leastUsedEntry);
|
delete map[leastUsedEntry.key];
|
|
removed = leastUsedEntry.value;
|
this._lastRemovedEntry = leastUsedEntry;
|
}
|
|
if (entry) {
|
entry.value = value;
|
}
|
else {
|
entry = new Entry(value);
|
}
|
entry.key = key;
|
list.insertEntry(entry);
|
map[key] = entry;
|
}
|
|
return removed;
|
};
|
|
/**
|
* @param {string} key
|
* @return {}
|
*/
|
LRUProto.get = function (key) {
|
var entry = this._map[key];
|
var list = this._list;
|
if (entry != null) {
|
// Put the latest used entry in the tail
|
if (entry !== list.tail) {
|
list.remove(entry);
|
list.insertEntry(entry);
|
}
|
|
return entry.value;
|
}
|
};
|
|
/**
|
* Clear the cache
|
*/
|
LRUProto.clear = function () {
|
this._list.clear();
|
this._map = {};
|
};
|
|
module.exports = LRU;
|
|
|
/***/ },
|
/* 41 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var config = __webpack_require__(42);
|
|
/**
|
* @exports zrender/tool/log
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
*/
|
module.exports = function() {
|
if (config.debugMode === 0) {
|
return;
|
}
|
else if (config.debugMode == 1) {
|
for (var k in arguments) {
|
throw new Error(arguments[k]);
|
}
|
}
|
else if (config.debugMode > 1) {
|
for (var k in arguments) {
|
console.log(arguments[k]);
|
}
|
}
|
};
|
|
/* for debug
|
return function(mes) {
|
document.getElementById('wrong-message').innerHTML =
|
mes + ' ' + (new Date() - 0)
|
+ '<br/>'
|
+ document.getElementById('wrong-message').innerHTML;
|
};
|
*/
|
|
|
|
/***/ },
|
/* 42 */
|
/***/ function(module, exports) {
|
|
|
var dpr = 1;
|
// If in browser environment
|
if (typeof window !== 'undefined') {
|
dpr = Math.max(window.devicePixelRatio || 1, 1);
|
}
|
/**
|
* config默认配置项
|
* @exports zrender/config
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
*/
|
var config = {
|
/**
|
* debug日志选项:catchBrushException为true下有效
|
* 0 : 不生成debug数据,发布用
|
* 1 : 异常抛出,调试用
|
* 2 : 控制台输出,调试用
|
*/
|
debugMode: 0,
|
|
// retina 屏幕优化
|
devicePixelRatio: dpr
|
};
|
module.exports = config;
|
|
|
|
|
/***/ },
|
/* 43 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var Group = __webpack_require__(30);
|
var componentUtil = __webpack_require__(20);
|
var clazzUtil = __webpack_require__(13);
|
var modelUtil = __webpack_require__(5);
|
var zrUtil = __webpack_require__(4);
|
|
function Chart() {
|
|
/**
|
* @type {module:zrender/container/Group}
|
* @readOnly
|
*/
|
this.group = new Group();
|
|
/**
|
* @type {string}
|
* @readOnly
|
*/
|
this.uid = componentUtil.getUID('viewChart');
|
}
|
|
Chart.prototype = {
|
|
type: 'chart',
|
|
/**
|
* Init the chart
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
init: function (ecModel, api) {},
|
|
/**
|
* Render the chart
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} payload
|
*/
|
render: function (seriesModel, ecModel, api, payload) {},
|
|
/**
|
* Highlight series or specified data item
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} payload
|
*/
|
highlight: function (seriesModel, ecModel, api, payload) {
|
toggleHighlight(seriesModel.getData(), payload, 'emphasis');
|
},
|
|
/**
|
* Downplay series or specified data item
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} payload
|
*/
|
downplay: function (seriesModel, ecModel, api, payload) {
|
toggleHighlight(seriesModel.getData(), payload, 'normal');
|
},
|
|
/**
|
* Remove self
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
remove: function (ecModel, api) {
|
this.group.removeAll();
|
},
|
|
/**
|
* Dispose self
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
dispose: function () {}
|
|
/**
|
* The view contains the given point.
|
* @interface
|
* @param {Array.<number>} point
|
* @return {boolean}
|
*/
|
// containPoint: function () {}
|
|
};
|
|
var chartProto = Chart.prototype;
|
chartProto.updateView
|
= chartProto.updateLayout
|
= chartProto.updateVisual
|
= function (seriesModel, ecModel, api, payload) {
|
this.render(seriesModel, ecModel, api, payload);
|
};
|
|
/**
|
* Set state of single element
|
* @param {module:zrender/Element} el
|
* @param {string} state
|
*/
|
function elSetState(el, state) {
|
if (el) {
|
el.trigger(state);
|
if (el.type === 'group') {
|
for (var i = 0; i < el.childCount(); i++) {
|
elSetState(el.childAt(i), state);
|
}
|
}
|
}
|
}
|
/**
|
* @param {module:echarts/data/List} data
|
* @param {Object} payload
|
* @param {string} state 'normal'|'emphasis'
|
* @inner
|
*/
|
function toggleHighlight(data, payload, state) {
|
var dataIndex = modelUtil.queryDataIndex(data, payload);
|
|
if (dataIndex != null) {
|
zrUtil.each(modelUtil.normalizeToArray(dataIndex), function (dataIdx) {
|
elSetState(data.getItemGraphicEl(dataIdx), state);
|
});
|
}
|
else {
|
data.eachItemGraphicEl(function (el) {
|
elSetState(el, state);
|
});
|
}
|
}
|
|
// Enable Chart.extend.
|
clazzUtil.enableClassExtend(Chart, ['dispose']);
|
|
// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.
|
clazzUtil.enableClassManagement(Chart, {registerWhenExtend: true});
|
|
module.exports = Chart;
|
|
|
/***/ },
|
/* 44 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var pathTool = __webpack_require__(45);
|
var round = Math.round;
|
var Path = __webpack_require__(46);
|
var colorTool = __webpack_require__(39);
|
var matrix = __webpack_require__(11);
|
var vector = __webpack_require__(10);
|
var Transformable = __webpack_require__(34);
|
var BoundingRect = __webpack_require__(9);
|
|
var graphic = {};
|
|
graphic.Group = __webpack_require__(30);
|
|
graphic.Image = __webpack_require__(62);
|
|
graphic.Text = __webpack_require__(63);
|
|
graphic.Circle = __webpack_require__(64);
|
|
graphic.Sector = __webpack_require__(65);
|
|
graphic.Ring = __webpack_require__(66);
|
|
graphic.Polygon = __webpack_require__(67);
|
|
graphic.Polyline = __webpack_require__(71);
|
|
graphic.Rect = __webpack_require__(72);
|
|
graphic.Line = __webpack_require__(74);
|
|
graphic.BezierCurve = __webpack_require__(75);
|
|
graphic.Arc = __webpack_require__(76);
|
|
graphic.CompoundPath = __webpack_require__(77);
|
|
graphic.LinearGradient = __webpack_require__(78);
|
|
graphic.RadialGradient = __webpack_require__(80);
|
|
graphic.BoundingRect = BoundingRect;
|
|
/**
|
* Extend shape with parameters
|
*/
|
graphic.extendShape = function (opts) {
|
return Path.extend(opts);
|
};
|
|
/**
|
* Extend path
|
*/
|
graphic.extendPath = function (pathData, opts) {
|
return pathTool.extendFromString(pathData, opts);
|
};
|
|
/**
|
* Create a path element from path data string
|
* @param {string} pathData
|
* @param {Object} opts
|
* @param {module:zrender/core/BoundingRect} rect
|
* @param {string} [layout=cover] 'center' or 'cover'
|
*/
|
graphic.makePath = function (pathData, opts, rect, layout) {
|
var path = pathTool.createFromString(pathData, opts);
|
var boundingRect = path.getBoundingRect();
|
if (rect) {
|
var aspect = boundingRect.width / boundingRect.height;
|
|
if (layout === 'center') {
|
// Set rect to center, keep width / height ratio.
|
var width = rect.height * aspect;
|
var height;
|
if (width <= rect.width) {
|
height = rect.height;
|
}
|
else {
|
width = rect.width;
|
height = width / aspect;
|
}
|
var cx = rect.x + rect.width / 2;
|
var cy = rect.y + rect.height / 2;
|
|
rect.x = cx - width / 2;
|
rect.y = cy - height / 2;
|
rect.width = width;
|
rect.height = height;
|
}
|
|
graphic.resizePath(path, rect);
|
}
|
return path;
|
};
|
|
graphic.mergePath = pathTool.mergePath,
|
|
/**
|
* Resize a path to fit the rect
|
* @param {module:zrender/graphic/Path} path
|
* @param {Object} rect
|
*/
|
graphic.resizePath = function (path, rect) {
|
if (!path.applyTransform) {
|
return;
|
}
|
|
var pathRect = path.getBoundingRect();
|
|
var m = pathRect.calculateTransform(rect);
|
|
path.applyTransform(m);
|
};
|
|
/**
|
* Sub pixel optimize line for canvas
|
*
|
* @param {Object} param
|
* @param {Object} [param.shape]
|
* @param {number} [param.shape.x1]
|
* @param {number} [param.shape.y1]
|
* @param {number} [param.shape.x2]
|
* @param {number} [param.shape.y2]
|
* @param {Object} [param.style]
|
* @param {number} [param.style.lineWidth]
|
* @return {Object} Modified param
|
*/
|
graphic.subPixelOptimizeLine = function (param) {
|
var subPixelOptimize = graphic.subPixelOptimize;
|
var shape = param.shape;
|
var lineWidth = param.style.lineWidth;
|
|
if (round(shape.x1 * 2) === round(shape.x2 * 2)) {
|
shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true);
|
}
|
if (round(shape.y1 * 2) === round(shape.y2 * 2)) {
|
shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true);
|
}
|
return param;
|
};
|
|
/**
|
* Sub pixel optimize rect for canvas
|
*
|
* @param {Object} param
|
* @param {Object} [param.shape]
|
* @param {number} [param.shape.x]
|
* @param {number} [param.shape.y]
|
* @param {number} [param.shape.width]
|
* @param {number} [param.shape.height]
|
* @param {Object} [param.style]
|
* @param {number} [param.style.lineWidth]
|
* @return {Object} Modified param
|
*/
|
graphic.subPixelOptimizeRect = function (param) {
|
var subPixelOptimize = graphic.subPixelOptimize;
|
var shape = param.shape;
|
var lineWidth = param.style.lineWidth;
|
var originX = shape.x;
|
var originY = shape.y;
|
var originWidth = shape.width;
|
var originHeight = shape.height;
|
shape.x = subPixelOptimize(shape.x, lineWidth, true);
|
shape.y = subPixelOptimize(shape.y, lineWidth, true);
|
shape.width = Math.max(
|
subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x,
|
originWidth === 0 ? 0 : 1
|
);
|
shape.height = Math.max(
|
subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y,
|
originHeight === 0 ? 0 : 1
|
);
|
return param;
|
};
|
|
/**
|
* Sub pixel optimize for canvas
|
*
|
* @param {number} position Coordinate, such as x, y
|
* @param {number} lineWidth Should be nonnegative integer.
|
* @param {boolean=} positiveOrNegative Default false (negative).
|
* @return {number} Optimized position.
|
*/
|
graphic.subPixelOptimize = function (position, lineWidth, positiveOrNegative) {
|
// Assure that (position + lineWidth / 2) is near integer edge,
|
// otherwise line will be fuzzy in canvas.
|
var doubledPosition = round(position * 2);
|
return (doubledPosition + round(lineWidth)) % 2 === 0
|
? doubledPosition / 2
|
: (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2;
|
};
|
|
function hasFillOrStroke(fillOrStroke) {
|
return fillOrStroke != null && fillOrStroke != 'none';
|
}
|
|
function liftColor(color) {
|
return typeof color === 'string' ? colorTool.lift(color, -0.1) : color;
|
}
|
|
/**
|
* @private
|
*/
|
function cacheElementStl(el) {
|
if (el.__hoverStlDirty) {
|
var stroke = el.style.stroke;
|
var fill = el.style.fill;
|
|
// Create hoverStyle on mouseover
|
var hoverStyle = el.__hoverStl;
|
hoverStyle.fill = hoverStyle.fill
|
|| (hasFillOrStroke(fill) ? liftColor(fill) : null);
|
hoverStyle.stroke = hoverStyle.stroke
|
|| (hasFillOrStroke(stroke) ? liftColor(stroke) : null);
|
|
var normalStyle = {};
|
for (var name in hoverStyle) {
|
if (hoverStyle.hasOwnProperty(name)) {
|
normalStyle[name] = el.style[name];
|
}
|
}
|
|
el.__normalStl = normalStyle;
|
|
el.__hoverStlDirty = false;
|
}
|
}
|
|
/**
|
* @private
|
*/
|
function doSingleEnterHover(el) {
|
if (el.__isHover) {
|
return;
|
}
|
|
cacheElementStl(el);
|
|
if (el.useHoverLayer) {
|
el.__zr && el.__zr.addHover(el, el.__hoverStl);
|
}
|
else {
|
el.setStyle(el.__hoverStl);
|
el.z2 += 1;
|
}
|
|
el.__isHover = true;
|
}
|
|
/**
|
* @inner
|
*/
|
function doSingleLeaveHover(el) {
|
if (!el.__isHover) {
|
return;
|
}
|
|
var normalStl = el.__normalStl;
|
if (el.useHoverLayer) {
|
el.__zr && el.__zr.removeHover(el);
|
}
|
else {
|
normalStl && el.setStyle(normalStl);
|
el.z2 -= 1;
|
}
|
|
el.__isHover = false;
|
}
|
|
/**
|
* @inner
|
*/
|
function doEnterHover(el) {
|
el.type === 'group'
|
? el.traverse(function (child) {
|
if (child.type !== 'group') {
|
doSingleEnterHover(child);
|
}
|
})
|
: doSingleEnterHover(el);
|
}
|
|
function doLeaveHover(el) {
|
el.type === 'group'
|
? el.traverse(function (child) {
|
if (child.type !== 'group') {
|
doSingleLeaveHover(child);
|
}
|
})
|
: doSingleLeaveHover(el);
|
}
|
|
/**
|
* @inner
|
*/
|
function setElementHoverStl(el, hoverStl) {
|
// If element has sepcified hoverStyle, then use it instead of given hoverStyle
|
// Often used when item group has a label element and it's hoverStyle is different
|
el.__hoverStl = el.hoverStyle || hoverStl || {};
|
el.__hoverStlDirty = true;
|
|
if (el.__isHover) {
|
cacheElementStl(el);
|
}
|
}
|
|
/**
|
* @inner
|
*/
|
function onElementMouseOver(e) {
|
if (this.__hoverSilentOnTouch && e.zrByTouch) {
|
return;
|
}
|
|
// Only if element is not in emphasis status
|
!this.__isEmphasis && doEnterHover(this);
|
}
|
|
/**
|
* @inner
|
*/
|
function onElementMouseOut(e) {
|
if (this.__hoverSilentOnTouch && e.zrByTouch) {
|
return;
|
}
|
|
// Only if element is not in emphasis status
|
!this.__isEmphasis && doLeaveHover(this);
|
}
|
|
/**
|
* @inner
|
*/
|
function enterEmphasis() {
|
this.__isEmphasis = true;
|
doEnterHover(this);
|
}
|
|
/**
|
* @inner
|
*/
|
function leaveEmphasis() {
|
this.__isEmphasis = false;
|
doLeaveHover(this);
|
}
|
|
/**
|
* Set hover style of element.
|
* This method can be called repeatly without side-effects.
|
* @param {module:zrender/Element} el
|
* @param {Object} [hoverStyle]
|
* @param {Object} [opt]
|
* @param {boolean} [opt.hoverSilentOnTouch=false]
|
* In touch device, mouseover event will be trigger on touchstart event
|
* (see module:zrender/dom/HandlerProxy). By this mechanism, we can
|
* conviniently use hoverStyle when tap on touch screen without additional
|
* code for compatibility.
|
* But if the chart/component has select feature, which usually also use
|
* hoverStyle, there might be conflict between 'select-highlight' and
|
* 'hover-highlight' especially when roam is enabled (see geo for example).
|
* In this case, hoverSilentOnTouch should be used to disable hover-highlight
|
* on touch device.
|
*/
|
graphic.setHoverStyle = function (el, hoverStyle, opt) {
|
el.__hoverSilentOnTouch = opt && opt.hoverSilentOnTouch;
|
|
el.type === 'group'
|
? el.traverse(function (child) {
|
if (child.type !== 'group') {
|
setElementHoverStl(child, hoverStyle);
|
}
|
})
|
: setElementHoverStl(el, hoverStyle);
|
|
// Duplicated function will be auto-ignored, see Eventful.js.
|
el.on('mouseover', onElementMouseOver)
|
.on('mouseout', onElementMouseOut);
|
|
// Emphasis, normal can be triggered manually
|
el.on('emphasis', enterEmphasis)
|
.on('normal', leaveEmphasis);
|
};
|
|
/**
|
* Set text option in the style
|
* @param {Object} textStyle
|
* @param {module:echarts/model/Model} labelModel
|
* @param {string} color
|
*/
|
graphic.setText = function (textStyle, labelModel, color) {
|
var labelPosition = labelModel.getShallow('position') || 'inside';
|
var labelOffset = labelModel.getShallow('offset');
|
var labelColor = labelPosition.indexOf('inside') >= 0 ? 'white' : color;
|
var textStyleModel = labelModel.getModel('textStyle');
|
zrUtil.extend(textStyle, {
|
textDistance: labelModel.getShallow('distance') || 5,
|
textFont: textStyleModel.getFont(),
|
textPosition: labelPosition,
|
textOffset: labelOffset,
|
textFill: textStyleModel.getTextColor() || labelColor
|
});
|
};
|
|
function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) {
|
if (typeof dataIndex === 'function') {
|
cb = dataIndex;
|
dataIndex = null;
|
}
|
// Do not check 'animation' property directly here. Consider this case:
|
// animation model is an `itemModel`, whose does not have `isAnimationEnabled`
|
// but its parent model (`seriesModel`) does.
|
var animationEnabled = animatableModel && animatableModel.isAnimationEnabled();
|
|
if (animationEnabled) {
|
var postfix = isUpdate ? 'Update' : '';
|
var duration = animatableModel.getShallow('animationDuration' + postfix);
|
var animationEasing = animatableModel.getShallow('animationEasing' + postfix);
|
var animationDelay = animatableModel.getShallow('animationDelay' + postfix);
|
if (typeof animationDelay === 'function') {
|
animationDelay = animationDelay(
|
dataIndex,
|
animatableModel.getAnimationDelayParams
|
? animatableModel.getAnimationDelayParams(el, dataIndex)
|
: null
|
);
|
}
|
if (typeof duration === 'function') {
|
duration = duration(dataIndex);
|
}
|
|
duration > 0
|
? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb)
|
: (el.stopAnimation(), el.attr(props), cb && cb());
|
}
|
else {
|
el.stopAnimation();
|
el.attr(props);
|
cb && cb();
|
}
|
}
|
|
/**
|
* Update graphic element properties with or without animation according to the configuration in series
|
* @param {module:zrender/Element} el
|
* @param {Object} props
|
* @param {module:echarts/model/Model} [animatableModel]
|
* @param {number} [dataIndex]
|
* @param {Function} [cb]
|
* @example
|
* graphic.updateProps(el, {
|
* position: [100, 100]
|
* }, seriesModel, dataIndex, function () { console.log('Animation done!'); });
|
* // Or
|
* graphic.updateProps(el, {
|
* position: [100, 100]
|
* }, seriesModel, function () { console.log('Animation done!'); });
|
*/
|
graphic.updateProps = function (el, props, animatableModel, dataIndex, cb) {
|
animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
|
};
|
|
/**
|
* Init graphic element properties with or without animation according to the configuration in series
|
* @param {module:zrender/Element} el
|
* @param {Object} props
|
* @param {module:echarts/model/Model} [animatableModel]
|
* @param {number} [dataIndex]
|
* @param {Function} cb
|
*/
|
graphic.initProps = function (el, props, animatableModel, dataIndex, cb) {
|
animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
|
};
|
|
/**
|
* Get transform matrix of target (param target),
|
* in coordinate of its ancestor (param ancestor)
|
*
|
* @param {module:zrender/mixin/Transformable} target
|
* @param {module:zrender/mixin/Transformable} [ancestor]
|
*/
|
graphic.getTransform = function (target, ancestor) {
|
var mat = matrix.identity([]);
|
|
while (target && target !== ancestor) {
|
matrix.mul(mat, target.getLocalTransform(), mat);
|
target = target.parent;
|
}
|
|
return mat;
|
};
|
|
/**
|
* Apply transform to an vertex.
|
* @param {Array.<number>} target [x, y]
|
* @param {Array.<number>|TypedArray.<number>|Object} transform Can be:
|
* + Transform matrix: like [1, 0, 0, 1, 0, 0]
|
* + {position, rotation, scale}, the same as `zrender/Transformable`.
|
* @param {boolean=} invert Whether use invert matrix.
|
* @return {Array.<number>} [x, y]
|
*/
|
graphic.applyTransform = function (target, transform, invert) {
|
if (transform && !zrUtil.isArrayLike(transform)) {
|
transform = Transformable.getLocalTransform(transform);
|
}
|
|
if (invert) {
|
transform = matrix.invert([], transform);
|
}
|
return vector.applyTransform([], target, transform);
|
};
|
|
/**
|
* @param {string} direction 'left' 'right' 'top' 'bottom'
|
* @param {Array.<number>} transform Transform matrix: like [1, 0, 0, 1, 0, 0]
|
* @param {boolean=} invert Whether use invert matrix.
|
* @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'
|
*/
|
graphic.transformDirection = function (direction, transform, invert) {
|
|
// Pick a base, ensure that transform result will not be (0, 0).
|
var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0)
|
? 1 : Math.abs(2 * transform[4] / transform[0]);
|
var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0)
|
? 1 : Math.abs(2 * transform[4] / transform[2]);
|
|
var vertex = [
|
direction === 'left' ? -hBase : direction === 'right' ? hBase : 0,
|
direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0
|
];
|
|
vertex = graphic.applyTransform(vertex, transform, invert);
|
|
return Math.abs(vertex[0]) > Math.abs(vertex[1])
|
? (vertex[0] > 0 ? 'right' : 'left')
|
: (vertex[1] > 0 ? 'bottom' : 'top');
|
};
|
|
/**
|
* Apply group transition animation from g1 to g2.
|
* If no animatableModel, no animation.
|
*/
|
graphic.groupTransition = function (g1, g2, animatableModel, cb) {
|
if (!g1 || !g2) {
|
return;
|
}
|
|
function getElMap(g) {
|
var elMap = {};
|
g.traverse(function (el) {
|
if (!el.isGroup && el.anid) {
|
elMap[el.anid] = el;
|
}
|
});
|
return elMap;
|
}
|
function getAnimatableProps(el) {
|
var obj = {
|
position: vector.clone(el.position),
|
rotation: el.rotation
|
};
|
if (el.shape) {
|
obj.shape = zrUtil.extend({}, el.shape);
|
}
|
return obj;
|
}
|
var elMap1 = getElMap(g1);
|
|
g2.traverse(function (el) {
|
if (!el.isGroup && el.anid) {
|
var oldEl = elMap1[el.anid];
|
if (oldEl) {
|
var newProp = getAnimatableProps(el);
|
el.attr(getAnimatableProps(oldEl));
|
graphic.updateProps(el, newProp, animatableModel, el.dataIndex);
|
}
|
// else {
|
// if (el.previousProps) {
|
// graphic.updateProps
|
// }
|
// }
|
}
|
});
|
};
|
|
module.exports = graphic;
|
|
|
|
/***/ },
|
/* 45 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var Path = __webpack_require__(46);
|
var PathProxy = __webpack_require__(50);
|
var transformPath = __webpack_require__(61);
|
|
// command chars
|
var cc = [
|
'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z',
|
'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A'
|
];
|
|
var mathSqrt = Math.sqrt;
|
var mathSin = Math.sin;
|
var mathCos = Math.cos;
|
var PI = Math.PI;
|
|
var vMag = function(v) {
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
};
|
var vRatio = function(u, v) {
|
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
|
};
|
var vAngle = function(u, v) {
|
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1)
|
* Math.acos(vRatio(u, v));
|
};
|
|
function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) {
|
var psi = psiDeg * (PI / 180.0);
|
var xp = mathCos(psi) * (x1 - x2) / 2.0
|
+ mathSin(psi) * (y1 - y2) / 2.0;
|
var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0
|
+ mathCos(psi) * (y1 - y2) / 2.0;
|
|
var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
|
|
if (lambda > 1) {
|
rx *= mathSqrt(lambda);
|
ry *= mathSqrt(lambda);
|
}
|
|
var f = (fa === fs ? -1 : 1)
|
* mathSqrt((((rx * rx) * (ry * ry))
|
- ((rx * rx) * (yp * yp))
|
- ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp)
|
+ (ry * ry) * (xp * xp))
|
) || 0;
|
|
var cxp = f * rx * yp / ry;
|
var cyp = f * -ry * xp / rx;
|
|
var cx = (x1 + x2) / 2.0
|
+ mathCos(psi) * cxp
|
- mathSin(psi) * cyp;
|
var cy = (y1 + y2) / 2.0
|
+ mathSin(psi) * cxp
|
+ mathCos(psi) * cyp;
|
|
var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]);
|
var u = [ (xp - cxp) / rx, (yp - cyp) / ry ];
|
var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ];
|
var dTheta = vAngle(u, v);
|
|
if (vRatio(u, v) <= -1) {
|
dTheta = PI;
|
}
|
if (vRatio(u, v) >= 1) {
|
dTheta = 0;
|
}
|
if (fs === 0 && dTheta > 0) {
|
dTheta = dTheta - 2 * PI;
|
}
|
if (fs === 1 && dTheta < 0) {
|
dTheta = dTheta + 2 * PI;
|
}
|
|
path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs);
|
}
|
|
function createPathProxyFromString(data) {
|
if (!data) {
|
return [];
|
}
|
|
// command string
|
var cs = data.replace(/-/g, ' -')
|
.replace(/ /g, ' ')
|
.replace(/ /g, ',')
|
.replace(/,,/g, ',');
|
|
var n;
|
// create pipes so that we can split the data
|
for (n = 0; n < cc.length; n++) {
|
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
}
|
|
// create array
|
var arr = cs.split('|');
|
// init context point
|
var cpx = 0;
|
var cpy = 0;
|
|
var path = new PathProxy();
|
var CMD = PathProxy.CMD;
|
|
var prevCmd;
|
for (n = 1; n < arr.length; n++) {
|
var str = arr[n];
|
var c = str.charAt(0);
|
var off = 0;
|
var p = str.slice(1).replace(/e,-/g, 'e-').split(',');
|
var cmd;
|
|
if (p.length > 0 && p[0] === '') {
|
p.shift();
|
}
|
|
for (var i = 0; i < p.length; i++) {
|
p[i] = parseFloat(p[i]);
|
}
|
while (off < p.length && !isNaN(p[off])) {
|
if (isNaN(p[0])) {
|
break;
|
}
|
var ctlPtx;
|
var ctlPty;
|
|
var rx;
|
var ry;
|
var psi;
|
var fa;
|
var fs;
|
|
var x1 = cpx;
|
var y1 = cpy;
|
|
// convert l, H, h, V, and v to L
|
switch (c) {
|
case 'l':
|
cpx += p[off++];
|
cpy += p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'L':
|
cpx = p[off++];
|
cpy = p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'm':
|
cpx += p[off++];
|
cpy += p[off++];
|
cmd = CMD.M;
|
path.addData(cmd, cpx, cpy);
|
c = 'l';
|
break;
|
case 'M':
|
cpx = p[off++];
|
cpy = p[off++];
|
cmd = CMD.M;
|
path.addData(cmd, cpx, cpy);
|
c = 'L';
|
break;
|
case 'h':
|
cpx += p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'H':
|
cpx = p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'v':
|
cpy += p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'V':
|
cpy = p[off++];
|
cmd = CMD.L;
|
path.addData(cmd, cpx, cpy);
|
break;
|
case 'C':
|
cmd = CMD.C;
|
path.addData(
|
cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]
|
);
|
cpx = p[off - 2];
|
cpy = p[off - 1];
|
break;
|
case 'c':
|
cmd = CMD.C;
|
path.addData(
|
cmd,
|
p[off++] + cpx, p[off++] + cpy,
|
p[off++] + cpx, p[off++] + cpy,
|
p[off++] + cpx, p[off++] + cpy
|
);
|
cpx += p[off - 2];
|
cpy += p[off - 1];
|
break;
|
case 'S':
|
ctlPtx = cpx;
|
ctlPty = cpy;
|
var len = path.len();
|
var pathData = path.data;
|
if (prevCmd === CMD.C) {
|
ctlPtx += cpx - pathData[len - 4];
|
ctlPty += cpy - pathData[len - 3];
|
}
|
cmd = CMD.C;
|
x1 = p[off++];
|
y1 = p[off++];
|
cpx = p[off++];
|
cpy = p[off++];
|
path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
|
break;
|
case 's':
|
ctlPtx = cpx;
|
ctlPty = cpy;
|
var len = path.len();
|
var pathData = path.data;
|
if (prevCmd === CMD.C) {
|
ctlPtx += cpx - pathData[len - 4];
|
ctlPty += cpy - pathData[len - 3];
|
}
|
cmd = CMD.C;
|
x1 = cpx + p[off++];
|
y1 = cpy + p[off++];
|
cpx += p[off++];
|
cpy += p[off++];
|
path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy);
|
break;
|
case 'Q':
|
x1 = p[off++];
|
y1 = p[off++];
|
cpx = p[off++];
|
cpy = p[off++];
|
cmd = CMD.Q;
|
path.addData(cmd, x1, y1, cpx, cpy);
|
break;
|
case 'q':
|
x1 = p[off++] + cpx;
|
y1 = p[off++] + cpy;
|
cpx += p[off++];
|
cpy += p[off++];
|
cmd = CMD.Q;
|
path.addData(cmd, x1, y1, cpx, cpy);
|
break;
|
case 'T':
|
ctlPtx = cpx;
|
ctlPty = cpy;
|
var len = path.len();
|
var pathData = path.data;
|
if (prevCmd === CMD.Q) {
|
ctlPtx += cpx - pathData[len - 4];
|
ctlPty += cpy - pathData[len - 3];
|
}
|
cpx = p[off++];
|
cpy = p[off++];
|
cmd = CMD.Q;
|
path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
|
break;
|
case 't':
|
ctlPtx = cpx;
|
ctlPty = cpy;
|
var len = path.len();
|
var pathData = path.data;
|
if (prevCmd === CMD.Q) {
|
ctlPtx += cpx - pathData[len - 4];
|
ctlPty += cpy - pathData[len - 3];
|
}
|
cpx += p[off++];
|
cpy += p[off++];
|
cmd = CMD.Q;
|
path.addData(cmd, ctlPtx, ctlPty, cpx, cpy);
|
break;
|
case 'A':
|
rx = p[off++];
|
ry = p[off++];
|
psi = p[off++];
|
fa = p[off++];
|
fs = p[off++];
|
|
x1 = cpx, y1 = cpy;
|
cpx = p[off++];
|
cpy = p[off++];
|
cmd = CMD.A;
|
processArc(
|
x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
|
);
|
break;
|
case 'a':
|
rx = p[off++];
|
ry = p[off++];
|
psi = p[off++];
|
fa = p[off++];
|
fs = p[off++];
|
|
x1 = cpx, y1 = cpy;
|
cpx += p[off++];
|
cpy += p[off++];
|
cmd = CMD.A;
|
processArc(
|
x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path
|
);
|
break;
|
}
|
}
|
|
if (c === 'z' || c === 'Z') {
|
cmd = CMD.Z;
|
path.addData(cmd);
|
}
|
|
prevCmd = cmd;
|
}
|
|
path.toStatic();
|
|
return path;
|
}
|
|
// TODO Optimize double memory cost problem
|
function createPathOptions(str, opts) {
|
var pathProxy = createPathProxyFromString(str);
|
opts = opts || {};
|
opts.buildPath = function (path) {
|
if (path.setData) {
|
path.setData(pathProxy.data);
|
// Svg and vml renderer don't have context
|
var ctx = path.getContext();
|
if (ctx) {
|
path.rebuildPath(ctx);
|
}
|
}
|
else {
|
var ctx = path;
|
pathProxy.rebuildPath(ctx);
|
}
|
};
|
|
opts.applyTransform = function (m) {
|
transformPath(pathProxy, m);
|
|
this.dirty(true);
|
};
|
|
return opts;
|
}
|
|
module.exports = {
|
/**
|
* Create a Path object from path string data
|
* http://www.w3.org/TR/SVG/paths.html#PathData
|
* @param {Object} opts Other options
|
*/
|
createFromString: function (str, opts) {
|
return new Path(createPathOptions(str, opts));
|
},
|
|
/**
|
* Create a Path class from path string data
|
* @param {string} str
|
* @param {Object} opts Other options
|
*/
|
extendFromString: function (str, opts) {
|
return Path.extend(createPathOptions(str, opts));
|
},
|
|
/**
|
* Merge multiple paths
|
*/
|
// TODO Apply transform
|
// TODO stroke dash
|
// TODO Optimize double memory cost problem
|
mergePath: function (pathEls, opts) {
|
var pathList = [];
|
var len = pathEls.length;
|
for (var i = 0; i < len; i++) {
|
var pathEl = pathEls[i];
|
if (!pathEl.path) {
|
pathEl.createPathProxy();
|
}
|
if (pathEl.__dirtyPath) {
|
pathEl.buildPath(pathEl.path, pathEl.shape, true);
|
}
|
pathList.push(pathEl.path);
|
}
|
|
var pathBundle = new Path(opts);
|
// Need path proxy.
|
pathBundle.createPathProxy();
|
pathBundle.buildPath = function (path) {
|
path.appendPath(pathList);
|
// Svg and vml renderer don't have context
|
var ctx = path.getContext();
|
if (ctx) {
|
path.rebuildPath(ctx);
|
}
|
};
|
|
return pathBundle;
|
}
|
};
|
|
|
/***/ },
|
/* 46 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Path element
|
* @module zrender/graphic/Path
|
*/
|
|
|
|
var Displayable = __webpack_require__(47);
|
var zrUtil = __webpack_require__(4);
|
var PathProxy = __webpack_require__(50);
|
var pathContain = __webpack_require__(53);
|
|
var Pattern = __webpack_require__(60);
|
var getCanvasPattern = Pattern.prototype.getCanvasPattern;
|
|
var abs = Math.abs;
|
|
var pathProxyForDraw = new PathProxy(true);
|
/**
|
* @alias module:zrender/graphic/Path
|
* @extends module:zrender/graphic/Displayable
|
* @constructor
|
* @param {Object} opts
|
*/
|
function Path(opts) {
|
Displayable.call(this, opts);
|
|
/**
|
* @type {module:zrender/core/PathProxy}
|
* @readOnly
|
*/
|
this.path = null;
|
}
|
|
Path.prototype = {
|
|
constructor: Path,
|
|
type: 'path',
|
|
__dirtyPath: true,
|
|
strokeContainThreshold: 5,
|
|
brush: function (ctx, prevEl) {
|
var style = this.style;
|
var path = this.path || pathProxyForDraw;
|
var hasStroke = style.hasStroke();
|
var hasFill = style.hasFill();
|
var fill = style.fill;
|
var stroke = style.stroke;
|
var hasFillGradient = hasFill && !!(fill.colorStops);
|
var hasStrokeGradient = hasStroke && !!(stroke.colorStops);
|
var hasFillPattern = hasFill && !!(fill.image);
|
var hasStrokePattern = hasStroke && !!(stroke.image);
|
|
style.bind(ctx, this, prevEl);
|
this.setTransform(ctx);
|
|
if (this.__dirty) {
|
var rect;
|
// Update gradient because bounding rect may changed
|
if (hasFillGradient) {
|
rect = rect || this.getBoundingRect();
|
this._fillGradient = style.getGradient(ctx, fill, rect);
|
}
|
if (hasStrokeGradient) {
|
rect = rect || this.getBoundingRect();
|
this._strokeGradient = style.getGradient(ctx, stroke, rect);
|
}
|
}
|
// Use the gradient or pattern
|
if (hasFillGradient) {
|
// PENDING If may have affect the state
|
ctx.fillStyle = this._fillGradient;
|
}
|
else if (hasFillPattern) {
|
ctx.fillStyle = getCanvasPattern.call(fill, ctx);
|
}
|
if (hasStrokeGradient) {
|
ctx.strokeStyle = this._strokeGradient;
|
}
|
else if (hasStrokePattern) {
|
ctx.strokeStyle = getCanvasPattern.call(stroke, ctx);
|
}
|
|
var lineDash = style.lineDash;
|
var lineDashOffset = style.lineDashOffset;
|
|
var ctxLineDash = !!ctx.setLineDash;
|
|
// Update path sx, sy
|
var scale = this.getGlobalScale();
|
path.setScale(scale[0], scale[1]);
|
|
// Proxy context
|
// Rebuild path in following 2 cases
|
// 1. Path is dirty
|
// 2. Path needs javascript implemented lineDash stroking.
|
// In this case, lineDash information will not be saved in PathProxy
|
if (this.__dirtyPath
|
|| (lineDash && !ctxLineDash && hasStroke)
|
) {
|
path.beginPath(ctx);
|
|
// Setting line dash before build path
|
if (lineDash && !ctxLineDash) {
|
path.setLineDash(lineDash);
|
path.setLineDashOffset(lineDashOffset);
|
}
|
|
this.buildPath(path, this.shape, false);
|
|
// Clear path dirty flag
|
if (this.path) {
|
this.__dirtyPath = false;
|
}
|
}
|
else {
|
// Replay path building
|
ctx.beginPath();
|
this.path.rebuildPath(ctx);
|
}
|
|
hasFill && path.fill(ctx);
|
|
if (lineDash && ctxLineDash) {
|
ctx.setLineDash(lineDash);
|
ctx.lineDashOffset = lineDashOffset;
|
}
|
|
hasStroke && path.stroke(ctx);
|
|
if (lineDash && ctxLineDash) {
|
// PENDING
|
// Remove lineDash
|
ctx.setLineDash([]);
|
}
|
|
|
this.restoreTransform(ctx);
|
|
// Draw rect text
|
if (style.text != null) {
|
// var rect = this.getBoundingRect();
|
this.drawRectText(ctx, this.getBoundingRect());
|
}
|
},
|
|
// When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath
|
// Like in circle
|
buildPath: function (ctx, shapeCfg, inBundle) {},
|
|
createPathProxy: function () {
|
this.path = new PathProxy();
|
},
|
|
getBoundingRect: function () {
|
var rect = this._rect;
|
var style = this.style;
|
var needsUpdateRect = !rect;
|
if (needsUpdateRect) {
|
var path = this.path;
|
if (!path) {
|
// Create path on demand.
|
path = this.path = new PathProxy();
|
}
|
if (this.__dirtyPath) {
|
path.beginPath();
|
this.buildPath(path, this.shape, false);
|
}
|
rect = path.getBoundingRect();
|
}
|
this._rect = rect;
|
|
if (style.hasStroke()) {
|
// Needs update rect with stroke lineWidth when
|
// 1. Element changes scale or lineWidth
|
// 2. Shape is changed
|
var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone());
|
if (this.__dirty || needsUpdateRect) {
|
rectWithStroke.copy(rect);
|
// FIXME Must after updateTransform
|
var w = style.lineWidth;
|
// PENDING, Min line width is needed when line is horizontal or vertical
|
var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
|
|
// Only add extra hover lineWidth when there are no fill
|
if (!style.hasFill()) {
|
w = Math.max(w, this.strokeContainThreshold || 4);
|
}
|
// Consider line width
|
// Line scale can't be 0;
|
if (lineScale > 1e-10) {
|
rectWithStroke.width += w / lineScale;
|
rectWithStroke.height += w / lineScale;
|
rectWithStroke.x -= w / lineScale / 2;
|
rectWithStroke.y -= w / lineScale / 2;
|
}
|
}
|
|
// Return rect with stroke
|
return rectWithStroke;
|
}
|
|
return rect;
|
},
|
|
contain: function (x, y) {
|
var localPos = this.transformCoordToLocal(x, y);
|
var rect = this.getBoundingRect();
|
var style = this.style;
|
x = localPos[0];
|
y = localPos[1];
|
|
if (rect.contain(x, y)) {
|
var pathData = this.path.data;
|
if (style.hasStroke()) {
|
var lineWidth = style.lineWidth;
|
var lineScale = style.strokeNoScale ? this.getLineScale() : 1;
|
// Line scale can't be 0;
|
if (lineScale > 1e-10) {
|
// Only add extra hover lineWidth when there are no fill
|
if (!style.hasFill()) {
|
lineWidth = Math.max(lineWidth, this.strokeContainThreshold);
|
}
|
if (pathContain.containStroke(
|
pathData, lineWidth / lineScale, x, y
|
)) {
|
return true;
|
}
|
}
|
}
|
if (style.hasFill()) {
|
return pathContain.contain(pathData, x, y);
|
}
|
}
|
return false;
|
},
|
|
/**
|
* @param {boolean} dirtyPath
|
*/
|
dirty: function (dirtyPath) {
|
if (dirtyPath == null) {
|
dirtyPath = true;
|
}
|
// Only mark dirty, not mark clean
|
if (dirtyPath) {
|
this.__dirtyPath = dirtyPath;
|
this._rect = null;
|
}
|
|
this.__dirty = true;
|
|
this.__zr && this.__zr.refresh();
|
|
// Used as a clipping path
|
if (this.__clipTarget) {
|
this.__clipTarget.dirty();
|
}
|
},
|
|
/**
|
* Alias for animate('shape')
|
* @param {boolean} loop
|
*/
|
animateShape: function (loop) {
|
return this.animate('shape', loop);
|
},
|
|
// Overwrite attrKV
|
attrKV: function (key, value) {
|
// FIXME
|
if (key === 'shape') {
|
this.setShape(value);
|
this.__dirtyPath = true;
|
this._rect = null;
|
}
|
else {
|
Displayable.prototype.attrKV.call(this, key, value);
|
}
|
},
|
|
/**
|
* @param {Object|string} key
|
* @param {*} value
|
*/
|
setShape: function (key, value) {
|
var shape = this.shape;
|
// Path from string may not have shape
|
if (shape) {
|
if (zrUtil.isObject(key)) {
|
for (var name in key) {
|
if (key.hasOwnProperty(name)) {
|
shape[name] = key[name];
|
}
|
}
|
}
|
else {
|
shape[key] = value;
|
}
|
this.dirty(true);
|
}
|
return this;
|
},
|
|
getLineScale: function () {
|
var m = this.transform;
|
// Get the line scale.
|
// Determinant of `m` means how much the area is enlarged by the
|
// transformation. So its square root can be used as a scale factor
|
// for width.
|
return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10
|
? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1]))
|
: 1;
|
}
|
};
|
|
/**
|
* 扩展一个 Path element, 比如星形,圆等。
|
* Extend a path element
|
* @param {Object} props
|
* @param {string} props.type Path type
|
* @param {Function} props.init Initialize
|
* @param {Function} props.buildPath Overwrite buildPath method
|
* @param {Object} [props.style] Extended default style config
|
* @param {Object} [props.shape] Extended default shape config
|
*/
|
Path.extend = function (defaults) {
|
var Sub = function (opts) {
|
Path.call(this, opts);
|
|
if (defaults.style) {
|
// Extend default style
|
this.style.extendFrom(defaults.style, false);
|
}
|
|
// Extend default shape
|
var defaultShape = defaults.shape;
|
if (defaultShape) {
|
this.shape = this.shape || {};
|
var thisShape = this.shape;
|
for (var name in defaultShape) {
|
if (
|
! thisShape.hasOwnProperty(name)
|
&& defaultShape.hasOwnProperty(name)
|
) {
|
thisShape[name] = defaultShape[name];
|
}
|
}
|
}
|
|
defaults.init && defaults.init.call(this, opts);
|
};
|
|
zrUtil.inherits(Sub, Path);
|
|
// FIXME 不能 extend position, rotation 等引用对象
|
for (var name in defaults) {
|
// Extending prototype values and methods
|
if (name !== 'style' && name !== 'shape') {
|
Sub.prototype[name] = defaults[name];
|
}
|
}
|
|
return Sub;
|
};
|
|
zrUtil.inherits(Path, Displayable);
|
|
module.exports = Path;
|
|
|
/***/ },
|
/* 47 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 可绘制的图形基类
|
* Base class of all displayable graphic objects
|
* @module zrender/graphic/Displayable
|
*/
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var Style = __webpack_require__(48);
|
|
var Element = __webpack_require__(31);
|
var RectText = __webpack_require__(49);
|
// var Stateful = require('./mixin/Stateful');
|
|
/**
|
* @alias module:zrender/graphic/Displayable
|
* @extends module:zrender/Element
|
* @extends module:zrender/graphic/mixin/RectText
|
*/
|
function Displayable(opts) {
|
|
opts = opts || {};
|
|
Element.call(this, opts);
|
|
// Extend properties
|
for (var name in opts) {
|
if (
|
opts.hasOwnProperty(name) &&
|
name !== 'style'
|
) {
|
this[name] = opts[name];
|
}
|
}
|
|
/**
|
* @type {module:zrender/graphic/Style}
|
*/
|
this.style = new Style(opts.style);
|
|
this._rect = null;
|
// Shapes for cascade clipping.
|
this.__clipPaths = [];
|
|
// FIXME Stateful must be mixined after style is setted
|
// Stateful.call(this, opts);
|
}
|
|
Displayable.prototype = {
|
|
constructor: Displayable,
|
|
type: 'displayable',
|
|
/**
|
* Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制
|
* Dirty flag. From which painter will determine if this displayable object needs brush
|
* @name module:zrender/graphic/Displayable#__dirty
|
* @type {boolean}
|
*/
|
__dirty: true,
|
|
/**
|
* 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件
|
* If ignore drawing of the displayable object. Mouse event will still be triggered
|
* @name module:/zrender/graphic/Displayable#invisible
|
* @type {boolean}
|
* @default false
|
*/
|
invisible: false,
|
|
/**
|
* @name module:/zrender/graphic/Displayable#z
|
* @type {number}
|
* @default 0
|
*/
|
z: 0,
|
|
/**
|
* @name module:/zrender/graphic/Displayable#z
|
* @type {number}
|
* @default 0
|
*/
|
z2: 0,
|
|
/**
|
* z层level,决定绘画在哪层canvas中
|
* @name module:/zrender/graphic/Displayable#zlevel
|
* @type {number}
|
* @default 0
|
*/
|
zlevel: 0,
|
|
/**
|
* 是否可拖拽
|
* @name module:/zrender/graphic/Displayable#draggable
|
* @type {boolean}
|
* @default false
|
*/
|
draggable: false,
|
|
/**
|
* 是否正在拖拽
|
* @name module:/zrender/graphic/Displayable#draggable
|
* @type {boolean}
|
* @default false
|
*/
|
dragging: false,
|
|
/**
|
* 是否相应鼠标事件
|
* @name module:/zrender/graphic/Displayable#silent
|
* @type {boolean}
|
* @default false
|
*/
|
silent: false,
|
|
/**
|
* If enable culling
|
* @type {boolean}
|
* @default false
|
*/
|
culling: false,
|
|
/**
|
* Mouse cursor when hovered
|
* @name module:/zrender/graphic/Displayable#cursor
|
* @type {string}
|
*/
|
cursor: 'pointer',
|
|
/**
|
* If hover area is bounding rect
|
* @name module:/zrender/graphic/Displayable#rectHover
|
* @type {string}
|
*/
|
rectHover: false,
|
|
/**
|
* Render the element progressively when the value >= 0,
|
* usefull for large data.
|
* @type {number}
|
*/
|
progressive: -1,
|
|
beforeBrush: function (ctx) {},
|
|
afterBrush: function (ctx) {},
|
|
/**
|
* 图形绘制方法
|
* @param {Canvas2DRenderingContext} ctx
|
*/
|
// Interface
|
brush: function (ctx, prevEl) {},
|
|
/**
|
* 获取最小包围盒
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
// Interface
|
getBoundingRect: function () {},
|
|
/**
|
* 判断坐标 x, y 是否在图形上
|
* If displayable element contain coord x, y
|
* @param {number} x
|
* @param {number} y
|
* @return {boolean}
|
*/
|
contain: function (x, y) {
|
return this.rectContain(x, y);
|
},
|
|
/**
|
* @param {Function} cb
|
* @param {} context
|
*/
|
traverse: function (cb, context) {
|
cb.call(context, this);
|
},
|
|
/**
|
* 判断坐标 x, y 是否在图形的包围盒上
|
* If bounding rect of element contain coord x, y
|
* @param {number} x
|
* @param {number} y
|
* @return {boolean}
|
*/
|
rectContain: function (x, y) {
|
var coord = this.transformCoordToLocal(x, y);
|
var rect = this.getBoundingRect();
|
return rect.contain(coord[0], coord[1]);
|
},
|
|
/**
|
* 标记图形元素为脏,并且在下一帧重绘
|
* Mark displayable element dirty and refresh next frame
|
*/
|
dirty: function () {
|
this.__dirty = true;
|
|
this._rect = null;
|
|
this.__zr && this.__zr.refresh();
|
},
|
|
/**
|
* 图形是否会触发事件
|
* If displayable object binded any event
|
* @return {boolean}
|
*/
|
// TODO, 通过 bind 绑定的事件
|
// isSilent: function () {
|
// return !(
|
// this.hoverable || this.draggable
|
// || this.onmousemove || this.onmouseover || this.onmouseout
|
// || this.onmousedown || this.onmouseup || this.onclick
|
// || this.ondragenter || this.ondragover || this.ondragleave
|
// || this.ondrop
|
// );
|
// },
|
/**
|
* Alias for animate('style')
|
* @param {boolean} loop
|
*/
|
animateStyle: function (loop) {
|
return this.animate('style', loop);
|
},
|
|
attrKV: function (key, value) {
|
if (key !== 'style') {
|
Element.prototype.attrKV.call(this, key, value);
|
}
|
else {
|
this.style.set(value);
|
}
|
},
|
|
/**
|
* @param {Object|string} key
|
* @param {*} value
|
*/
|
setStyle: function (key, value) {
|
this.style.set(key, value);
|
this.dirty(false);
|
return this;
|
},
|
|
/**
|
* Use given style object
|
* @param {Object} obj
|
*/
|
useStyle: function (obj) {
|
this.style = new Style(obj);
|
this.dirty(false);
|
return this;
|
}
|
};
|
|
zrUtil.inherits(Displayable, Element);
|
|
zrUtil.mixin(Displayable, RectText);
|
// zrUtil.mixin(Displayable, Stateful);
|
|
module.exports = Displayable;
|
|
|
/***/ },
|
/* 48 */
|
/***/ function(module, exports) {
|
|
/**
|
* @module zrender/graphic/Style
|
*/
|
|
|
var STYLE_COMMON_PROPS = [
|
['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
|
['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
|
];
|
|
// var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
|
// var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);
|
|
var Style = function (opts) {
|
this.extendFrom(opts);
|
};
|
|
function createLinearGradient(ctx, obj, rect) {
|
var x = obj.x == null ? 0 : obj.x;
|
var x2 = obj.x2 == null ? 1 : obj.x2;
|
var y = obj.y == null ? 0 : obj.y;
|
var y2 = obj.y2 == null ? 0 : obj.y2;
|
|
if (!obj.global) {
|
x = x * rect.width + rect.x;
|
x2 = x2 * rect.width + rect.x;
|
y = y * rect.height + rect.y;
|
y2 = y2 * rect.height + rect.y;
|
}
|
|
var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);
|
|
return canvasGradient;
|
}
|
|
function createRadialGradient(ctx, obj, rect) {
|
var width = rect.width;
|
var height = rect.height;
|
var min = Math.min(width, height);
|
|
var x = obj.x == null ? 0.5 : obj.x;
|
var y = obj.y == null ? 0.5 : obj.y;
|
var r = obj.r == null ? 0.5 : obj.r;
|
if (!obj.global) {
|
x = x * width + rect.x;
|
y = y * height + rect.y;
|
r = r * min;
|
}
|
|
var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);
|
|
return canvasGradient;
|
}
|
|
|
Style.prototype = {
|
|
constructor: Style,
|
|
/**
|
* @type {string}
|
*/
|
fill: '#000000',
|
|
/**
|
* @type {string}
|
*/
|
stroke: null,
|
|
/**
|
* @type {number}
|
*/
|
opacity: 1,
|
|
/**
|
* @type {Array.<number>}
|
*/
|
lineDash: null,
|
|
/**
|
* @type {number}
|
*/
|
lineDashOffset: 0,
|
|
/**
|
* @type {number}
|
*/
|
shadowBlur: 0,
|
|
/**
|
* @type {number}
|
*/
|
shadowOffsetX: 0,
|
|
/**
|
* @type {number}
|
*/
|
shadowOffsetY: 0,
|
|
/**
|
* @type {number}
|
*/
|
lineWidth: 1,
|
|
/**
|
* If stroke ignore scale
|
* @type {Boolean}
|
*/
|
strokeNoScale: false,
|
|
// Bounding rect text configuration
|
// Not affected by element transform
|
/**
|
* @type {string}
|
*/
|
text: null,
|
|
/**
|
* @type {string}
|
*/
|
textFill: '#000',
|
|
/**
|
* @type {string}
|
*/
|
textStroke: null,
|
|
/**
|
* 'inside', 'left', 'right', 'top', 'bottom'
|
* [x, y]
|
* @type {string|Array.<number>}
|
* @default 'inside'
|
*/
|
textPosition: 'inside',
|
|
/**
|
* [x, y]
|
* @type {Array.<number>}
|
*/
|
textOffset: null,
|
|
/**
|
* @type {string}
|
*/
|
textBaseline: null,
|
|
/**
|
* @type {string}
|
*/
|
textAlign: null,
|
|
/**
|
* @type {string}
|
*/
|
textVerticalAlign: null,
|
|
/**
|
* Only useful in Path and Image element
|
* @type {number}
|
*/
|
textDistance: 5,
|
|
/**
|
* Only useful in Path and Image element
|
* @type {number}
|
*/
|
textShadowBlur: 0,
|
|
/**
|
* Only useful in Path and Image element
|
* @type {number}
|
*/
|
textShadowOffsetX: 0,
|
|
/**
|
* Only useful in Path and Image element
|
* @type {number}
|
*/
|
textShadowOffsetY: 0,
|
|
/**
|
* If transform text
|
* Only useful in Path and Image element
|
* @type {boolean}
|
*/
|
textTransform: false,
|
|
/**
|
* Text rotate around position of Path or Image
|
* Only useful in Path and Image element and textTransform is false.
|
*/
|
textRotation: 0,
|
|
/**
|
* @type {string}
|
* https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
|
*/
|
blend: null,
|
|
/**
|
* @param {CanvasRenderingContext2D} ctx
|
*/
|
bind: function (ctx, el, prevEl) {
|
var style = this;
|
var prevStyle = prevEl && prevEl.style;
|
var firstDraw = !prevStyle;
|
|
for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
|
var prop = STYLE_COMMON_PROPS[i];
|
var styleName = prop[0];
|
|
if (firstDraw || style[styleName] !== prevStyle[styleName]) {
|
// FIXME Invalid property value will cause style leak from previous element.
|
ctx[styleName] = style[styleName] || prop[1];
|
}
|
}
|
|
if ((firstDraw || style.fill !== prevStyle.fill)) {
|
ctx.fillStyle = style.fill;
|
}
|
if ((firstDraw || style.stroke !== prevStyle.stroke)) {
|
ctx.strokeStyle = style.stroke;
|
}
|
if ((firstDraw || style.opacity !== prevStyle.opacity)) {
|
ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
|
}
|
|
if ((firstDraw || style.blend !== prevStyle.blend)) {
|
ctx.globalCompositeOperation = style.blend || 'source-over';
|
}
|
if (this.hasStroke()) {
|
var lineWidth = style.lineWidth;
|
ctx.lineWidth = lineWidth / (
|
(this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
|
);
|
}
|
},
|
|
hasFill: function () {
|
var fill = this.fill;
|
return fill != null && fill !== 'none';
|
},
|
|
hasStroke: function () {
|
var stroke = this.stroke;
|
return stroke != null && stroke !== 'none' && this.lineWidth > 0;
|
},
|
|
/**
|
* Extend from other style
|
* @param {zrender/graphic/Style} otherStyle
|
* @param {boolean} overwrite
|
*/
|
extendFrom: function (otherStyle, overwrite) {
|
if (otherStyle) {
|
var target = this;
|
for (var name in otherStyle) {
|
if (otherStyle.hasOwnProperty(name)
|
&& (overwrite || ! target.hasOwnProperty(name))
|
) {
|
target[name] = otherStyle[name];
|
}
|
}
|
}
|
},
|
|
/**
|
* Batch setting style with a given object
|
* @param {Object|string} obj
|
* @param {*} [obj]
|
*/
|
set: function (obj, value) {
|
if (typeof obj === 'string') {
|
this[obj] = value;
|
}
|
else {
|
this.extendFrom(obj, true);
|
}
|
},
|
|
/**
|
* Clone
|
* @return {zrender/graphic/Style} [description]
|
*/
|
clone: function () {
|
var newStyle = new this.constructor();
|
newStyle.extendFrom(this, true);
|
return newStyle;
|
},
|
|
getGradient: function (ctx, obj, rect) {
|
var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
|
var canvasGradient = method(ctx, obj, rect);
|
var colorStops = obj.colorStops;
|
for (var i = 0; i < colorStops.length; i++) {
|
canvasGradient.addColorStop(
|
colorStops[i].offset, colorStops[i].color
|
);
|
}
|
return canvasGradient;
|
}
|
};
|
|
var styleProto = Style.prototype;
|
for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
|
var prop = STYLE_COMMON_PROPS[i];
|
if (!(prop[0] in styleProto)) {
|
styleProto[prop[0]] = prop[1];
|
}
|
}
|
|
// Provide for others
|
Style.getGradient = styleProto.getGradient;
|
|
module.exports = Style;
|
|
|
/***/ },
|
/* 49 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Mixin for drawing text in a element bounding rect
|
* @module zrender/mixin/RectText
|
*/
|
|
|
|
var textContain = __webpack_require__(8);
|
var BoundingRect = __webpack_require__(9);
|
|
var tmpRect = new BoundingRect();
|
|
var RectText = function () {};
|
|
function parsePercent(value, maxValue) {
|
if (typeof value === 'string') {
|
if (value.lastIndexOf('%') >= 0) {
|
return parseFloat(value) / 100 * maxValue;
|
}
|
return parseFloat(value);
|
}
|
return value;
|
}
|
|
RectText.prototype = {
|
|
constructor: RectText,
|
|
/**
|
* Draw text in a rect with specified position.
|
* @param {CanvasRenderingContext} ctx
|
* @param {Object} rect Displayable rect
|
* @return {Object} textRect Alternative precalculated text bounding rect
|
*/
|
drawRectText: function (ctx, rect, textRect) {
|
var style = this.style;
|
var text = style.text;
|
// Convert to string
|
text != null && (text += '');
|
if (!text) {
|
return;
|
}
|
|
// FIXME
|
ctx.save();
|
|
var x;
|
var y;
|
var textPosition = style.textPosition;
|
var textOffset = style.textOffset;
|
var distance = style.textDistance;
|
var align = style.textAlign;
|
var font = style.textFont || style.font;
|
var baseline = style.textBaseline;
|
var verticalAlign = style.textVerticalAlign;
|
|
textRect = textRect || textContain.getBoundingRect(text, font, align, baseline);
|
|
// Transform rect to view space
|
var transform = this.transform;
|
if (!style.textTransform) {
|
if (transform) {
|
tmpRect.copy(rect);
|
tmpRect.applyTransform(transform);
|
rect = tmpRect;
|
}
|
}
|
else {
|
this.setTransform(ctx);
|
}
|
|
// Text position represented by coord
|
if (textPosition instanceof Array) {
|
// Percent
|
x = rect.x + parsePercent(textPosition[0], rect.width);
|
y = rect.y + parsePercent(textPosition[1], rect.height);
|
align = align || 'left';
|
baseline = baseline || 'top';
|
|
if (verticalAlign) {
|
switch (verticalAlign) {
|
case 'middle':
|
y -= textRect.height / 2 - textRect.lineHeight / 2;
|
break;
|
case 'bottom':
|
y -= textRect.height - textRect.lineHeight / 2;
|
break;
|
default:
|
y += textRect.lineHeight / 2;
|
}
|
// Force bseline to be middle
|
baseline = 'middle';
|
}
|
}
|
else {
|
var res = textContain.adjustTextPositionOnRect(
|
textPosition, rect, textRect, distance
|
);
|
x = res.x;
|
y = res.y;
|
// Default align and baseline when has textPosition
|
align = align || res.textAlign;
|
baseline = baseline || res.textBaseline;
|
}
|
|
if (textOffset) {
|
x += textOffset[0];
|
y += textOffset[1];
|
}
|
|
// Use canvas default left textAlign. Giving invalid value will cause state not change
|
ctx.textAlign = align || 'left';
|
// Use canvas default alphabetic baseline
|
ctx.textBaseline = baseline || 'alphabetic';
|
|
var textFill = style.textFill;
|
var textStroke = style.textStroke;
|
textFill && (ctx.fillStyle = textFill);
|
textStroke && (ctx.strokeStyle = textStroke);
|
|
// TODO Invalid font
|
ctx.font = font || '12px sans-serif';
|
|
// Text shadow
|
// Always set shadowBlur and shadowOffset to avoid leak from displayable
|
ctx.shadowBlur = style.textShadowBlur;
|
ctx.shadowColor = style.textShadowColor || 'transparent';
|
ctx.shadowOffsetX = style.textShadowOffsetX;
|
ctx.shadowOffsetY = style.textShadowOffsetY;
|
|
var textLines = text.split('\n');
|
|
if (style.textRotation) {
|
transform && ctx.translate(transform[4], transform[5]);
|
ctx.rotate(style.textRotation);
|
transform && ctx.translate(-transform[4], -transform[5]);
|
}
|
|
for (var i = 0; i < textLines.length; i++) {
|
// Fill after stroke so the outline will not cover the main part.
|
textStroke && ctx.strokeText(textLines[i], x, y);
|
textFill && ctx.fillText(textLines[i], x, y);
|
y += textRect.lineHeight;
|
}
|
|
ctx.restore();
|
}
|
};
|
|
module.exports = RectText;
|
|
|
/***/ },
|
/* 50 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
|
* 可以用于 isInsidePath 判断以及获取boundingRect
|
*
|
* @module zrender/core/PathProxy
|
* @author Yi Shen (http://www.github.com/pissang)
|
*/
|
|
// TODO getTotalLength, getPointAtLength
|
|
|
var curve = __webpack_require__(51);
|
var vec2 = __webpack_require__(10);
|
var bbox = __webpack_require__(52);
|
var BoundingRect = __webpack_require__(9);
|
var dpr = __webpack_require__(42).devicePixelRatio;
|
|
var CMD = {
|
M: 1,
|
L: 2,
|
C: 3,
|
Q: 4,
|
A: 5,
|
Z: 6,
|
// Rect
|
R: 7
|
};
|
|
// var CMD_MEM_SIZE = {
|
// M: 3,
|
// L: 3,
|
// C: 7,
|
// Q: 5,
|
// A: 9,
|
// R: 5,
|
// Z: 1
|
// };
|
|
var min = [];
|
var max = [];
|
var min2 = [];
|
var max2 = [];
|
var mathMin = Math.min;
|
var mathMax = Math.max;
|
var mathCos = Math.cos;
|
var mathSin = Math.sin;
|
var mathSqrt = Math.sqrt;
|
var mathAbs = Math.abs;
|
|
var hasTypedArray = typeof Float32Array != 'undefined';
|
|
/**
|
* @alias module:zrender/core/PathProxy
|
* @constructor
|
*/
|
var PathProxy = function (notSaveData) {
|
|
this._saveData = !(notSaveData || false);
|
|
if (this._saveData) {
|
/**
|
* Path data. Stored as flat array
|
* @type {Array.<Object>}
|
*/
|
this.data = [];
|
}
|
|
this._ctx = null;
|
};
|
|
/**
|
* 快速计算Path包围盒(并不是最小包围盒)
|
* @return {Object}
|
*/
|
PathProxy.prototype = {
|
|
constructor: PathProxy,
|
|
_xi: 0,
|
_yi: 0,
|
|
_x0: 0,
|
_y0: 0,
|
// Unit x, Unit y. Provide for avoiding drawing that too short line segment
|
_ux: 0,
|
_uy: 0,
|
|
_len: 0,
|
|
_lineDash: null,
|
|
_dashOffset: 0,
|
|
_dashIdx: 0,
|
|
_dashSum: 0,
|
|
/**
|
* @readOnly
|
*/
|
setScale: function (sx, sy) {
|
this._ux = mathAbs(1 / dpr / sx) || 0;
|
this._uy = mathAbs(1 / dpr / sy) || 0;
|
},
|
|
getContext: function () {
|
return this._ctx;
|
},
|
|
/**
|
* @param {CanvasRenderingContext2D} ctx
|
* @return {module:zrender/core/PathProxy}
|
*/
|
beginPath: function (ctx) {
|
|
this._ctx = ctx;
|
|
ctx && ctx.beginPath();
|
|
ctx && (this.dpr = ctx.dpr);
|
|
// Reset
|
if (this._saveData) {
|
this._len = 0;
|
}
|
|
if (this._lineDash) {
|
this._lineDash = null;
|
|
this._dashOffset = 0;
|
}
|
|
return this;
|
},
|
|
/**
|
* @param {number} x
|
* @param {number} y
|
* @return {module:zrender/core/PathProxy}
|
*/
|
moveTo: function (x, y) {
|
this.addData(CMD.M, x, y);
|
this._ctx && this._ctx.moveTo(x, y);
|
|
// x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
|
// xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
|
// 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要
|
// 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持
|
this._x0 = x;
|
this._y0 = y;
|
|
this._xi = x;
|
this._yi = y;
|
|
return this;
|
},
|
|
/**
|
* @param {number} x
|
* @param {number} y
|
* @return {module:zrender/core/PathProxy}
|
*/
|
lineTo: function (x, y) {
|
var exceedUnit = mathAbs(x - this._xi) > this._ux
|
|| mathAbs(y - this._yi) > this._uy
|
// Force draw the first segment
|
|| this._len < 5;
|
|
this.addData(CMD.L, x, y);
|
|
if (this._ctx && exceedUnit) {
|
this._needsDash() ? this._dashedLineTo(x, y)
|
: this._ctx.lineTo(x, y);
|
}
|
if (exceedUnit) {
|
this._xi = x;
|
this._yi = y;
|
}
|
|
return this;
|
},
|
|
/**
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} x3
|
* @param {number} y3
|
* @return {module:zrender/core/PathProxy}
|
*/
|
bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
|
this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
|
if (this._ctx) {
|
this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3)
|
: this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
|
}
|
this._xi = x3;
|
this._yi = y3;
|
return this;
|
},
|
|
/**
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @return {module:zrender/core/PathProxy}
|
*/
|
quadraticCurveTo: function (x1, y1, x2, y2) {
|
this.addData(CMD.Q, x1, y1, x2, y2);
|
if (this._ctx) {
|
this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2)
|
: this._ctx.quadraticCurveTo(x1, y1, x2, y2);
|
}
|
this._xi = x2;
|
this._yi = y2;
|
return this;
|
},
|
|
/**
|
* @param {number} cx
|
* @param {number} cy
|
* @param {number} r
|
* @param {number} startAngle
|
* @param {number} endAngle
|
* @param {boolean} anticlockwise
|
* @return {module:zrender/core/PathProxy}
|
*/
|
arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) {
|
this.addData(
|
CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1
|
);
|
this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
|
|
this._xi = mathCos(endAngle) * r + cx;
|
this._yi = mathSin(endAngle) * r + cx;
|
return this;
|
},
|
|
// TODO
|
arcTo: function (x1, y1, x2, y2, radius) {
|
if (this._ctx) {
|
this._ctx.arcTo(x1, y1, x2, y2, radius);
|
}
|
return this;
|
},
|
|
// TODO
|
rect: function (x, y, w, h) {
|
this._ctx && this._ctx.rect(x, y, w, h);
|
this.addData(CMD.R, x, y, w, h);
|
return this;
|
},
|
|
/**
|
* @return {module:zrender/core/PathProxy}
|
*/
|
closePath: function () {
|
this.addData(CMD.Z);
|
|
var ctx = this._ctx;
|
var x0 = this._x0;
|
var y0 = this._y0;
|
if (ctx) {
|
this._needsDash() && this._dashedLineTo(x0, y0);
|
ctx.closePath();
|
}
|
|
this._xi = x0;
|
this._yi = y0;
|
return this;
|
},
|
|
/**
|
* Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。
|
* stroke 同样
|
* @param {CanvasRenderingContext2D} ctx
|
* @return {module:zrender/core/PathProxy}
|
*/
|
fill: function (ctx) {
|
ctx && ctx.fill();
|
this.toStatic();
|
},
|
|
/**
|
* @param {CanvasRenderingContext2D} ctx
|
* @return {module:zrender/core/PathProxy}
|
*/
|
stroke: function (ctx) {
|
ctx && ctx.stroke();
|
this.toStatic();
|
},
|
|
/**
|
* 必须在其它绘制命令前调用
|
* Must be invoked before all other path drawing methods
|
* @return {module:zrender/core/PathProxy}
|
*/
|
setLineDash: function (lineDash) {
|
if (lineDash instanceof Array) {
|
this._lineDash = lineDash;
|
|
this._dashIdx = 0;
|
|
var lineDashSum = 0;
|
for (var i = 0; i < lineDash.length; i++) {
|
lineDashSum += lineDash[i];
|
}
|
this._dashSum = lineDashSum;
|
}
|
return this;
|
},
|
|
/**
|
* 必须在其它绘制命令前调用
|
* Must be invoked before all other path drawing methods
|
* @return {module:zrender/core/PathProxy}
|
*/
|
setLineDashOffset: function (offset) {
|
this._dashOffset = offset;
|
return this;
|
},
|
|
/**
|
*
|
* @return {boolean}
|
*/
|
len: function () {
|
return this._len;
|
},
|
|
/**
|
* 直接设置 Path 数据
|
*/
|
setData: function (data) {
|
|
var len = data.length;
|
|
if (! (this.data && this.data.length == len) && hasTypedArray) {
|
this.data = new Float32Array(len);
|
}
|
|
for (var i = 0; i < len; i++) {
|
this.data[i] = data[i];
|
}
|
|
this._len = len;
|
},
|
|
/**
|
* 添加子路径
|
* @param {module:zrender/core/PathProxy|Array.<module:zrender/core/PathProxy>} path
|
*/
|
appendPath: function (path) {
|
if (!(path instanceof Array)) {
|
path = [path];
|
}
|
var len = path.length;
|
var appendSize = 0;
|
var offset = this._len;
|
for (var i = 0; i < len; i++) {
|
appendSize += path[i].len();
|
}
|
if (hasTypedArray && (this.data instanceof Float32Array)) {
|
this.data = new Float32Array(offset + appendSize);
|
}
|
for (var i = 0; i < len; i++) {
|
var appendPathData = path[i].data;
|
for (var k = 0; k < appendPathData.length; k++) {
|
this.data[offset++] = appendPathData[k];
|
}
|
}
|
this._len = offset;
|
},
|
|
/**
|
* 填充 Path 数据。
|
* 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
|
*/
|
addData: function (cmd) {
|
if (!this._saveData) {
|
return;
|
}
|
|
var data = this.data;
|
if (this._len + arguments.length > data.length) {
|
// 因为之前的数组已经转换成静态的 Float32Array
|
// 所以不够用时需要扩展一个新的动态数组
|
this._expandData();
|
data = this.data;
|
}
|
for (var i = 0; i < arguments.length; i++) {
|
data[this._len++] = arguments[i];
|
}
|
|
this._prevCmd = cmd;
|
},
|
|
_expandData: function () {
|
// Only if data is Float32Array
|
if (!(this.data instanceof Array)) {
|
var newData = [];
|
for (var i = 0; i < this._len; i++) {
|
newData[i] = this.data[i];
|
}
|
this.data = newData;
|
}
|
},
|
|
/**
|
* If needs js implemented dashed line
|
* @return {boolean}
|
* @private
|
*/
|
_needsDash: function () {
|
return this._lineDash;
|
},
|
|
_dashedLineTo: function (x1, y1) {
|
var dashSum = this._dashSum;
|
var offset = this._dashOffset;
|
var lineDash = this._lineDash;
|
var ctx = this._ctx;
|
|
var x0 = this._xi;
|
var y0 = this._yi;
|
var dx = x1 - x0;
|
var dy = y1 - y0;
|
var dist = mathSqrt(dx * dx + dy * dy);
|
var x = x0;
|
var y = y0;
|
var dash;
|
var nDash = lineDash.length;
|
var idx;
|
dx /= dist;
|
dy /= dist;
|
|
if (offset < 0) {
|
// Convert to positive offset
|
offset = dashSum + offset;
|
}
|
offset %= dashSum;
|
x -= offset * dx;
|
y -= offset * dy;
|
|
while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1)
|
|| (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) {
|
idx = this._dashIdx;
|
dash = lineDash[idx];
|
x += dx * dash;
|
y += dy * dash;
|
this._dashIdx = (idx + 1) % nDash;
|
// Skip positive offset
|
if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) {
|
continue;
|
}
|
ctx[idx % 2 ? 'moveTo' : 'lineTo'](
|
dx >= 0 ? mathMin(x, x1) : mathMax(x, x1),
|
dy >= 0 ? mathMin(y, y1) : mathMax(y, y1)
|
);
|
}
|
// Offset for next lineTo
|
dx = x - x1;
|
dy = y - y1;
|
this._dashOffset = -mathSqrt(dx * dx + dy * dy);
|
},
|
|
// Not accurate dashed line to
|
_dashedBezierTo: function (x1, y1, x2, y2, x3, y3) {
|
var dashSum = this._dashSum;
|
var offset = this._dashOffset;
|
var lineDash = this._lineDash;
|
var ctx = this._ctx;
|
|
var x0 = this._xi;
|
var y0 = this._yi;
|
var t;
|
var dx;
|
var dy;
|
var cubicAt = curve.cubicAt;
|
var bezierLen = 0;
|
var idx = this._dashIdx;
|
var nDash = lineDash.length;
|
|
var x;
|
var y;
|
|
var tmpLen = 0;
|
|
if (offset < 0) {
|
// Convert to positive offset
|
offset = dashSum + offset;
|
}
|
offset %= dashSum;
|
// Bezier approx length
|
for (t = 0; t < 1; t += 0.1) {
|
dx = cubicAt(x0, x1, x2, x3, t + 0.1)
|
- cubicAt(x0, x1, x2, x3, t);
|
dy = cubicAt(y0, y1, y2, y3, t + 0.1)
|
- cubicAt(y0, y1, y2, y3, t);
|
bezierLen += mathSqrt(dx * dx + dy * dy);
|
}
|
|
// Find idx after add offset
|
for (; idx < nDash; idx++) {
|
tmpLen += lineDash[idx];
|
if (tmpLen > offset) {
|
break;
|
}
|
}
|
t = (tmpLen - offset) / bezierLen;
|
|
while (t <= 1) {
|
|
x = cubicAt(x0, x1, x2, x3, t);
|
y = cubicAt(y0, y1, y2, y3, t);
|
|
// Use line to approximate dashed bezier
|
// Bad result if dash is long
|
idx % 2 ? ctx.moveTo(x, y)
|
: ctx.lineTo(x, y);
|
|
t += lineDash[idx] / bezierLen;
|
|
idx = (idx + 1) % nDash;
|
}
|
|
// Finish the last segment and calculate the new offset
|
(idx % 2 !== 0) && ctx.lineTo(x3, y3);
|
dx = x3 - x;
|
dy = y3 - y;
|
this._dashOffset = -mathSqrt(dx * dx + dy * dy);
|
},
|
|
_dashedQuadraticTo: function (x1, y1, x2, y2) {
|
// Convert quadratic to cubic using degree elevation
|
var x3 = x2;
|
var y3 = y2;
|
x2 = (x2 + 2 * x1) / 3;
|
y2 = (y2 + 2 * y1) / 3;
|
x1 = (this._xi + 2 * x1) / 3;
|
y1 = (this._yi + 2 * y1) / 3;
|
|
this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
|
},
|
|
/**
|
* 转成静态的 Float32Array 减少堆内存占用
|
* Convert dynamic array to static Float32Array
|
*/
|
toStatic: function () {
|
var data = this.data;
|
if (data instanceof Array) {
|
data.length = this._len;
|
if (hasTypedArray) {
|
this.data = new Float32Array(data);
|
}
|
}
|
},
|
|
/**
|
* @return {module:zrender/core/BoundingRect}
|
*/
|
getBoundingRect: function () {
|
min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;
|
max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
|
|
var data = this.data;
|
var xi = 0;
|
var yi = 0;
|
var x0 = 0;
|
var y0 = 0;
|
|
for (var i = 0; i < data.length;) {
|
var cmd = data[i++];
|
|
if (i == 1) {
|
// 如果第一个命令是 L, C, Q
|
// 则 previous point 同绘制命令的第一个 point
|
//
|
// 第一个命令为 Arc 的情况下会在后面特殊处理
|
xi = data[i];
|
yi = data[i + 1];
|
|
x0 = xi;
|
y0 = yi;
|
}
|
|
switch (cmd) {
|
case CMD.M:
|
// moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
|
// 在 closePath 的时候使用
|
x0 = data[i++];
|
y0 = data[i++];
|
xi = x0;
|
yi = y0;
|
min2[0] = x0;
|
min2[1] = y0;
|
max2[0] = x0;
|
max2[1] = y0;
|
break;
|
case CMD.L:
|
bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2);
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.C:
|
bbox.fromCubic(
|
xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
|
min2, max2
|
);
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.Q:
|
bbox.fromQuadratic(
|
xi, yi, data[i++], data[i++], data[i], data[i + 1],
|
min2, max2
|
);
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.A:
|
// TODO Arc 判断的开销比较大
|
var cx = data[i++];
|
var cy = data[i++];
|
var rx = data[i++];
|
var ry = data[i++];
|
var startAngle = data[i++];
|
var endAngle = data[i++] + startAngle;
|
// TODO Arc 旋转
|
var psi = data[i++];
|
var anticlockwise = 1 - data[i++];
|
|
if (i == 1) {
|
// 直接使用 arc 命令
|
// 第一个命令起点还未定义
|
x0 = mathCos(startAngle) * rx + cx;
|
y0 = mathSin(startAngle) * ry + cy;
|
}
|
|
bbox.fromArc(
|
cx, cy, rx, ry, startAngle, endAngle,
|
anticlockwise, min2, max2
|
);
|
|
xi = mathCos(endAngle) * rx + cx;
|
yi = mathSin(endAngle) * ry + cy;
|
break;
|
case CMD.R:
|
x0 = xi = data[i++];
|
y0 = yi = data[i++];
|
var width = data[i++];
|
var height = data[i++];
|
// Use fromLine
|
bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
|
break;
|
case CMD.Z:
|
xi = x0;
|
yi = y0;
|
break;
|
}
|
|
// Union
|
vec2.min(min, min, min2);
|
vec2.max(max, max, max2);
|
}
|
|
// No data
|
if (i === 0) {
|
min[0] = min[1] = max[0] = max[1] = 0;
|
}
|
|
return new BoundingRect(
|
min[0], min[1], max[0] - min[0], max[1] - min[1]
|
);
|
},
|
|
/**
|
* Rebuild path from current data
|
* Rebuild path will not consider javascript implemented line dash.
|
* @param {CanvasRenderingContext} ctx
|
*/
|
rebuildPath: function (ctx) {
|
var d = this.data;
|
var x0, y0;
|
var xi, yi;
|
var x, y;
|
var ux = this._ux;
|
var uy = this._uy;
|
var len = this._len;
|
for (var i = 0; i < len;) {
|
var cmd = d[i++];
|
|
if (i == 1) {
|
// 如果第一个命令是 L, C, Q
|
// 则 previous point 同绘制命令的第一个 point
|
//
|
// 第一个命令为 Arc 的情况下会在后面特殊处理
|
xi = d[i];
|
yi = d[i + 1];
|
|
x0 = xi;
|
y0 = yi;
|
}
|
switch (cmd) {
|
case CMD.M:
|
x0 = xi = d[i++];
|
y0 = yi = d[i++];
|
ctx.moveTo(xi, yi);
|
break;
|
case CMD.L:
|
x = d[i++];
|
y = d[i++];
|
// Not draw too small seg between
|
if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) {
|
ctx.lineTo(x, y);
|
xi = x;
|
yi = y;
|
}
|
break;
|
case CMD.C:
|
ctx.bezierCurveTo(
|
d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]
|
);
|
xi = d[i - 2];
|
yi = d[i - 1];
|
break;
|
case CMD.Q:
|
ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
|
xi = d[i - 2];
|
yi = d[i - 1];
|
break;
|
case CMD.A:
|
var cx = d[i++];
|
var cy = d[i++];
|
var rx = d[i++];
|
var ry = d[i++];
|
var theta = d[i++];
|
var dTheta = d[i++];
|
var psi = d[i++];
|
var fs = d[i++];
|
var r = (rx > ry) ? rx : ry;
|
var scaleX = (rx > ry) ? 1 : rx / ry;
|
var scaleY = (rx > ry) ? ry / rx : 1;
|
var isEllipse = Math.abs(rx - ry) > 1e-3;
|
var endAngle = theta + dTheta;
|
if (isEllipse) {
|
ctx.translate(cx, cy);
|
ctx.rotate(psi);
|
ctx.scale(scaleX, scaleY);
|
ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
|
ctx.scale(1 / scaleX, 1 / scaleY);
|
ctx.rotate(-psi);
|
ctx.translate(-cx, -cy);
|
}
|
else {
|
ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
|
}
|
|
if (i == 1) {
|
// 直接使用 arc 命令
|
// 第一个命令起点还未定义
|
x0 = mathCos(theta) * rx + cx;
|
y0 = mathSin(theta) * ry + cy;
|
}
|
xi = mathCos(endAngle) * rx + cx;
|
yi = mathSin(endAngle) * ry + cy;
|
break;
|
case CMD.R:
|
x0 = xi = d[i];
|
y0 = yi = d[i + 1];
|
ctx.rect(d[i++], d[i++], d[i++], d[i++]);
|
break;
|
case CMD.Z:
|
ctx.closePath();
|
xi = x0;
|
yi = y0;
|
}
|
}
|
}
|
};
|
|
PathProxy.CMD = CMD;
|
|
module.exports = PathProxy;
|
|
|
/***/ },
|
/* 51 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 曲线辅助模块
|
* @module zrender/core/curve
|
* @author pissang(https://www.github.com/pissang)
|
*/
|
|
|
var vec2 = __webpack_require__(10);
|
var v2Create = vec2.create;
|
var v2DistSquare = vec2.distSquare;
|
var mathPow = Math.pow;
|
var mathSqrt = Math.sqrt;
|
|
var EPSILON = 1e-8;
|
var EPSILON_NUMERIC = 1e-4;
|
|
var THREE_SQRT = mathSqrt(3);
|
var ONE_THIRD = 1 / 3;
|
|
// 临时变量
|
var _v0 = v2Create();
|
var _v1 = v2Create();
|
var _v2 = v2Create();
|
// var _v3 = vec2.create();
|
|
function isAroundZero(val) {
|
return val > -EPSILON && val < EPSILON;
|
}
|
function isNotAroundZero(val) {
|
return val > EPSILON || val < -EPSILON;
|
}
|
/**
|
* 计算三次贝塞尔值
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {number} t
|
* @return {number}
|
*/
|
function cubicAt(p0, p1, p2, p3, t) {
|
var onet = 1 - t;
|
return onet * onet * (onet * p0 + 3 * t * p1)
|
+ t * t * (t * p3 + 3 * onet * p2);
|
}
|
|
/**
|
* 计算三次贝塞尔导数值
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {number} t
|
* @return {number}
|
*/
|
function cubicDerivativeAt(p0, p1, p2, p3, t) {
|
var onet = 1 - t;
|
return 3 * (
|
((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet
|
+ (p3 - p2) * t * t
|
);
|
}
|
|
/**
|
* 计算三次贝塞尔方程根,使用盛金公式
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {number} val
|
* @param {Array.<number>} roots
|
* @return {number} 有效根数目
|
*/
|
function cubicRootAt(p0, p1, p2, p3, val, roots) {
|
// Evaluate roots of cubic functions
|
var a = p3 + 3 * (p1 - p2) - p0;
|
var b = 3 * (p2 - p1 * 2 + p0);
|
var c = 3 * (p1 - p0);
|
var d = p0 - val;
|
|
var A = b * b - 3 * a * c;
|
var B = b * c - 9 * a * d;
|
var C = c * c - 3 * b * d;
|
|
var n = 0;
|
|
if (isAroundZero(A) && isAroundZero(B)) {
|
if (isAroundZero(b)) {
|
roots[0] = 0;
|
}
|
else {
|
var t1 = -c / b; //t1, t2, t3, b is not zero
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
}
|
}
|
else {
|
var disc = B * B - 4 * A * C;
|
|
if (isAroundZero(disc)) {
|
var K = B / A;
|
var t1 = -b / a + K; // t1, a is not zero
|
var t2 = -K / 2; // t2, t3
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
if (t2 >= 0 && t2 <= 1) {
|
roots[n++] = t2;
|
}
|
}
|
else if (disc > 0) {
|
var discSqrt = mathSqrt(disc);
|
var Y1 = A * b + 1.5 * a * (-B + discSqrt);
|
var Y2 = A * b + 1.5 * a * (-B - discSqrt);
|
if (Y1 < 0) {
|
Y1 = -mathPow(-Y1, ONE_THIRD);
|
}
|
else {
|
Y1 = mathPow(Y1, ONE_THIRD);
|
}
|
if (Y2 < 0) {
|
Y2 = -mathPow(-Y2, ONE_THIRD);
|
}
|
else {
|
Y2 = mathPow(Y2, ONE_THIRD);
|
}
|
var t1 = (-b - (Y1 + Y2)) / (3 * a);
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
}
|
else {
|
var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));
|
var theta = Math.acos(T) / 3;
|
var ASqrt = mathSqrt(A);
|
var tmp = Math.cos(theta);
|
|
var t1 = (-b - 2 * ASqrt * tmp) / (3 * a);
|
var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);
|
var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
if (t2 >= 0 && t2 <= 1) {
|
roots[n++] = t2;
|
}
|
if (t3 >= 0 && t3 <= 1) {
|
roots[n++] = t3;
|
}
|
}
|
}
|
return n;
|
}
|
|
/**
|
* 计算三次贝塞尔方程极限值的位置
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {Array.<number>} extrema
|
* @return {number} 有效数目
|
*/
|
function cubicExtrema(p0, p1, p2, p3, extrema) {
|
var b = 6 * p2 - 12 * p1 + 6 * p0;
|
var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;
|
var c = 3 * p1 - 3 * p0;
|
|
var n = 0;
|
if (isAroundZero(a)) {
|
if (isNotAroundZero(b)) {
|
var t1 = -c / b;
|
if (t1 >= 0 && t1 <=1) {
|
extrema[n++] = t1;
|
}
|
}
|
}
|
else {
|
var disc = b * b - 4 * a * c;
|
if (isAroundZero(disc)) {
|
extrema[0] = -b / (2 * a);
|
}
|
else if (disc > 0) {
|
var discSqrt = mathSqrt(disc);
|
var t1 = (-b + discSqrt) / (2 * a);
|
var t2 = (-b - discSqrt) / (2 * a);
|
if (t1 >= 0 && t1 <= 1) {
|
extrema[n++] = t1;
|
}
|
if (t2 >= 0 && t2 <= 1) {
|
extrema[n++] = t2;
|
}
|
}
|
}
|
return n;
|
}
|
|
/**
|
* 细分三次贝塞尔曲线
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} p3
|
* @param {number} t
|
* @param {Array.<number>} out
|
*/
|
function cubicSubdivide(p0, p1, p2, p3, t, out) {
|
var p01 = (p1 - p0) * t + p0;
|
var p12 = (p2 - p1) * t + p1;
|
var p23 = (p3 - p2) * t + p2;
|
|
var p012 = (p12 - p01) * t + p01;
|
var p123 = (p23 - p12) * t + p12;
|
|
var p0123 = (p123 - p012) * t + p012;
|
// Seg0
|
out[0] = p0;
|
out[1] = p01;
|
out[2] = p012;
|
out[3] = p0123;
|
// Seg1
|
out[4] = p0123;
|
out[5] = p123;
|
out[6] = p23;
|
out[7] = p3;
|
}
|
|
/**
|
* 投射点到三次贝塞尔曲线上,返回投射距离。
|
* 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} x3
|
* @param {number} y3
|
* @param {number} x
|
* @param {number} y
|
* @param {Array.<number>} [out] 投射点
|
* @return {number}
|
*/
|
function cubicProjectPoint(
|
x0, y0, x1, y1, x2, y2, x3, y3,
|
x, y, out
|
) {
|
// http://pomax.github.io/bezierinfo/#projections
|
var t;
|
var interval = 0.005;
|
var d = Infinity;
|
var prev;
|
var next;
|
var d1;
|
var d2;
|
|
_v0[0] = x;
|
_v0[1] = y;
|
|
// 先粗略估计一下可能的最小距离的 t 值
|
// PENDING
|
for (var _t = 0; _t < 1; _t += 0.05) {
|
_v1[0] = cubicAt(x0, x1, x2, x3, _t);
|
_v1[1] = cubicAt(y0, y1, y2, y3, _t);
|
d1 = v2DistSquare(_v0, _v1);
|
if (d1 < d) {
|
t = _t;
|
d = d1;
|
}
|
}
|
d = Infinity;
|
|
// At most 32 iteration
|
for (var i = 0; i < 32; i++) {
|
if (interval < EPSILON_NUMERIC) {
|
break;
|
}
|
prev = t - interval;
|
next = t + interval;
|
// t - interval
|
_v1[0] = cubicAt(x0, x1, x2, x3, prev);
|
_v1[1] = cubicAt(y0, y1, y2, y3, prev);
|
|
d1 = v2DistSquare(_v1, _v0);
|
|
if (prev >= 0 && d1 < d) {
|
t = prev;
|
d = d1;
|
}
|
else {
|
// t + interval
|
_v2[0] = cubicAt(x0, x1, x2, x3, next);
|
_v2[1] = cubicAt(y0, y1, y2, y3, next);
|
d2 = v2DistSquare(_v2, _v0);
|
|
if (next <= 1 && d2 < d) {
|
t = next;
|
d = d2;
|
}
|
else {
|
interval *= 0.5;
|
}
|
}
|
}
|
// t
|
if (out) {
|
out[0] = cubicAt(x0, x1, x2, x3, t);
|
out[1] = cubicAt(y0, y1, y2, y3, t);
|
}
|
// console.log(interval, i);
|
return mathSqrt(d);
|
}
|
|
/**
|
* 计算二次方贝塞尔值
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} t
|
* @return {number}
|
*/
|
function quadraticAt(p0, p1, p2, t) {
|
var onet = 1 - t;
|
return onet * (onet * p0 + 2 * t * p1) + t * t * p2;
|
}
|
|
/**
|
* 计算二次方贝塞尔导数值
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} t
|
* @return {number}
|
*/
|
function quadraticDerivativeAt(p0, p1, p2, t) {
|
return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));
|
}
|
|
/**
|
* 计算二次方贝塞尔方程根
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} t
|
* @param {Array.<number>} roots
|
* @return {number} 有效根数目
|
*/
|
function quadraticRootAt(p0, p1, p2, val, roots) {
|
var a = p0 - 2 * p1 + p2;
|
var b = 2 * (p1 - p0);
|
var c = p0 - val;
|
|
var n = 0;
|
if (isAroundZero(a)) {
|
if (isNotAroundZero(b)) {
|
var t1 = -c / b;
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
}
|
}
|
else {
|
var disc = b * b - 4 * a * c;
|
if (isAroundZero(disc)) {
|
var t1 = -b / (2 * a);
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
}
|
else if (disc > 0) {
|
var discSqrt = mathSqrt(disc);
|
var t1 = (-b + discSqrt) / (2 * a);
|
var t2 = (-b - discSqrt) / (2 * a);
|
if (t1 >= 0 && t1 <= 1) {
|
roots[n++] = t1;
|
}
|
if (t2 >= 0 && t2 <= 1) {
|
roots[n++] = t2;
|
}
|
}
|
}
|
return n;
|
}
|
|
/**
|
* 计算二次贝塞尔方程极限值
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @return {number}
|
*/
|
function quadraticExtremum(p0, p1, p2) {
|
var divider = p0 + p2 - 2 * p1;
|
if (divider === 0) {
|
// p1 is center of p0 and p2
|
return 0.5;
|
}
|
else {
|
return (p0 - p1) / divider;
|
}
|
}
|
|
/**
|
* 细分二次贝塞尔曲线
|
* @memberOf module:zrender/core/curve
|
* @param {number} p0
|
* @param {number} p1
|
* @param {number} p2
|
* @param {number} t
|
* @param {Array.<number>} out
|
*/
|
function quadraticSubdivide(p0, p1, p2, t, out) {
|
var p01 = (p1 - p0) * t + p0;
|
var p12 = (p2 - p1) * t + p1;
|
var p012 = (p12 - p01) * t + p01;
|
|
// Seg0
|
out[0] = p0;
|
out[1] = p01;
|
out[2] = p012;
|
|
// Seg1
|
out[3] = p012;
|
out[4] = p12;
|
out[5] = p2;
|
}
|
|
/**
|
* 投射点到二次贝塞尔曲线上,返回投射距离。
|
* 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} x
|
* @param {number} y
|
* @param {Array.<number>} out 投射点
|
* @return {number}
|
*/
|
function quadraticProjectPoint(
|
x0, y0, x1, y1, x2, y2,
|
x, y, out
|
) {
|
// http://pomax.github.io/bezierinfo/#projections
|
var t;
|
var interval = 0.005;
|
var d = Infinity;
|
|
_v0[0] = x;
|
_v0[1] = y;
|
|
// 先粗略估计一下可能的最小距离的 t 值
|
// PENDING
|
for (var _t = 0; _t < 1; _t += 0.05) {
|
_v1[0] = quadraticAt(x0, x1, x2, _t);
|
_v1[1] = quadraticAt(y0, y1, y2, _t);
|
var d1 = v2DistSquare(_v0, _v1);
|
if (d1 < d) {
|
t = _t;
|
d = d1;
|
}
|
}
|
d = Infinity;
|
|
// At most 32 iteration
|
for (var i = 0; i < 32; i++) {
|
if (interval < EPSILON_NUMERIC) {
|
break;
|
}
|
var prev = t - interval;
|
var next = t + interval;
|
// t - interval
|
_v1[0] = quadraticAt(x0, x1, x2, prev);
|
_v1[1] = quadraticAt(y0, y1, y2, prev);
|
|
var d1 = v2DistSquare(_v1, _v0);
|
|
if (prev >= 0 && d1 < d) {
|
t = prev;
|
d = d1;
|
}
|
else {
|
// t + interval
|
_v2[0] = quadraticAt(x0, x1, x2, next);
|
_v2[1] = quadraticAt(y0, y1, y2, next);
|
var d2 = v2DistSquare(_v2, _v0);
|
if (next <= 1 && d2 < d) {
|
t = next;
|
d = d2;
|
}
|
else {
|
interval *= 0.5;
|
}
|
}
|
}
|
// t
|
if (out) {
|
out[0] = quadraticAt(x0, x1, x2, t);
|
out[1] = quadraticAt(y0, y1, y2, t);
|
}
|
// console.log(interval, i);
|
return mathSqrt(d);
|
}
|
|
module.exports = {
|
|
cubicAt: cubicAt,
|
|
cubicDerivativeAt: cubicDerivativeAt,
|
|
cubicRootAt: cubicRootAt,
|
|
cubicExtrema: cubicExtrema,
|
|
cubicSubdivide: cubicSubdivide,
|
|
cubicProjectPoint: cubicProjectPoint,
|
|
quadraticAt: quadraticAt,
|
|
quadraticDerivativeAt: quadraticDerivativeAt,
|
|
quadraticRootAt: quadraticRootAt,
|
|
quadraticExtremum: quadraticExtremum,
|
|
quadraticSubdivide: quadraticSubdivide,
|
|
quadraticProjectPoint: quadraticProjectPoint
|
};
|
|
|
/***/ },
|
/* 52 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @author Yi Shen(https://github.com/pissang)
|
*/
|
|
|
var vec2 = __webpack_require__(10);
|
var curve = __webpack_require__(51);
|
|
var bbox = {};
|
var mathMin = Math.min;
|
var mathMax = Math.max;
|
var mathSin = Math.sin;
|
var mathCos = Math.cos;
|
|
var start = vec2.create();
|
var end = vec2.create();
|
var extremity = vec2.create();
|
|
var PI2 = Math.PI * 2;
|
/**
|
* 从顶点数组中计算出最小包围盒,写入`min`和`max`中
|
* @module zrender/core/bbox
|
* @param {Array<Object>} points 顶点数组
|
* @param {number} min
|
* @param {number} max
|
*/
|
bbox.fromPoints = function(points, min, max) {
|
if (points.length === 0) {
|
return;
|
}
|
var p = points[0];
|
var left = p[0];
|
var right = p[0];
|
var top = p[1];
|
var bottom = p[1];
|
var i;
|
|
for (i = 1; i < points.length; i++) {
|
p = points[i];
|
left = mathMin(left, p[0]);
|
right = mathMax(right, p[0]);
|
top = mathMin(top, p[1]);
|
bottom = mathMax(bottom, p[1]);
|
}
|
|
min[0] = left;
|
min[1] = top;
|
max[0] = right;
|
max[1] = bottom;
|
};
|
|
/**
|
* @memberOf module:zrender/core/bbox
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {Array.<number>} min
|
* @param {Array.<number>} max
|
*/
|
bbox.fromLine = function (x0, y0, x1, y1, min, max) {
|
min[0] = mathMin(x0, x1);
|
min[1] = mathMin(y0, y1);
|
max[0] = mathMax(x0, x1);
|
max[1] = mathMax(y0, y1);
|
};
|
|
var xDim = [];
|
var yDim = [];
|
/**
|
* 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中
|
* @memberOf module:zrender/core/bbox
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} x3
|
* @param {number} y3
|
* @param {Array.<number>} min
|
* @param {Array.<number>} max
|
*/
|
bbox.fromCubic = function(
|
x0, y0, x1, y1, x2, y2, x3, y3, min, max
|
) {
|
var cubicExtrema = curve.cubicExtrema;
|
var cubicAt = curve.cubicAt;
|
var i;
|
var n = cubicExtrema(x0, x1, x2, x3, xDim);
|
min[0] = Infinity;
|
min[1] = Infinity;
|
max[0] = -Infinity;
|
max[1] = -Infinity;
|
|
for (i = 0; i < n; i++) {
|
var x = cubicAt(x0, x1, x2, x3, xDim[i]);
|
min[0] = mathMin(x, min[0]);
|
max[0] = mathMax(x, max[0]);
|
}
|
n = cubicExtrema(y0, y1, y2, y3, yDim);
|
for (i = 0; i < n; i++) {
|
var y = cubicAt(y0, y1, y2, y3, yDim[i]);
|
min[1] = mathMin(y, min[1]);
|
max[1] = mathMax(y, max[1]);
|
}
|
|
min[0] = mathMin(x0, min[0]);
|
max[0] = mathMax(x0, max[0]);
|
min[0] = mathMin(x3, min[0]);
|
max[0] = mathMax(x3, max[0]);
|
|
min[1] = mathMin(y0, min[1]);
|
max[1] = mathMax(y0, max[1]);
|
min[1] = mathMin(y3, min[1]);
|
max[1] = mathMax(y3, max[1]);
|
};
|
|
/**
|
* 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中
|
* @memberOf module:zrender/core/bbox
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {Array.<number>} min
|
* @param {Array.<number>} max
|
*/
|
bbox.fromQuadratic = function(x0, y0, x1, y1, x2, y2, min, max) {
|
var quadraticExtremum = curve.quadraticExtremum;
|
var quadraticAt = curve.quadraticAt;
|
// Find extremities, where derivative in x dim or y dim is zero
|
var tx =
|
mathMax(
|
mathMin(quadraticExtremum(x0, x1, x2), 1), 0
|
);
|
var ty =
|
mathMax(
|
mathMin(quadraticExtremum(y0, y1, y2), 1), 0
|
);
|
|
var x = quadraticAt(x0, x1, x2, tx);
|
var y = quadraticAt(y0, y1, y2, ty);
|
|
min[0] = mathMin(x0, x2, x);
|
min[1] = mathMin(y0, y2, y);
|
max[0] = mathMax(x0, x2, x);
|
max[1] = mathMax(y0, y2, y);
|
};
|
|
/**
|
* 从圆弧中计算出最小包围盒,写入`min`和`max`中
|
* @method
|
* @memberOf module:zrender/core/bbox
|
* @param {number} x
|
* @param {number} y
|
* @param {number} rx
|
* @param {number} ry
|
* @param {number} startAngle
|
* @param {number} endAngle
|
* @param {number} anticlockwise
|
* @param {Array.<number>} min
|
* @param {Array.<number>} max
|
*/
|
bbox.fromArc = function (
|
x, y, rx, ry, startAngle, endAngle, anticlockwise, min, max
|
) {
|
var vec2Min = vec2.min;
|
var vec2Max = vec2.max;
|
|
var diff = Math.abs(startAngle - endAngle);
|
|
|
if (diff % PI2 < 1e-4 && diff > 1e-4) {
|
// Is a circle
|
min[0] = x - rx;
|
min[1] = y - ry;
|
max[0] = x + rx;
|
max[1] = y + ry;
|
return;
|
}
|
|
start[0] = mathCos(startAngle) * rx + x;
|
start[1] = mathSin(startAngle) * ry + y;
|
|
end[0] = mathCos(endAngle) * rx + x;
|
end[1] = mathSin(endAngle) * ry + y;
|
|
vec2Min(min, start, end);
|
vec2Max(max, start, end);
|
|
// Thresh to [0, Math.PI * 2]
|
startAngle = startAngle % (PI2);
|
if (startAngle < 0) {
|
startAngle = startAngle + PI2;
|
}
|
endAngle = endAngle % (PI2);
|
if (endAngle < 0) {
|
endAngle = endAngle + PI2;
|
}
|
|
if (startAngle > endAngle && !anticlockwise) {
|
endAngle += PI2;
|
}
|
else if (startAngle < endAngle && anticlockwise) {
|
startAngle += PI2;
|
}
|
if (anticlockwise) {
|
var tmp = endAngle;
|
endAngle = startAngle;
|
startAngle = tmp;
|
}
|
|
// var number = 0;
|
// var step = (anticlockwise ? -Math.PI : Math.PI) / 2;
|
for (var angle = 0; angle < endAngle; angle += Math.PI / 2) {
|
if (angle > startAngle) {
|
extremity[0] = mathCos(angle) * rx + x;
|
extremity[1] = mathSin(angle) * ry + y;
|
|
vec2Min(min, extremity, min);
|
vec2Max(max, extremity, max);
|
}
|
}
|
};
|
|
module.exports = bbox;
|
|
|
|
/***/ },
|
/* 53 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var CMD = __webpack_require__(50).CMD;
|
var line = __webpack_require__(54);
|
var cubic = __webpack_require__(55);
|
var quadratic = __webpack_require__(56);
|
var arc = __webpack_require__(57);
|
var normalizeRadian = __webpack_require__(58).normalizeRadian;
|
var curve = __webpack_require__(51);
|
|
var windingLine = __webpack_require__(59);
|
|
var containStroke = line.containStroke;
|
|
var PI2 = Math.PI * 2;
|
|
var EPSILON = 1e-4;
|
|
function isAroundEqual(a, b) {
|
return Math.abs(a - b) < EPSILON;
|
}
|
|
// 临时数组
|
var roots = [-1, -1, -1];
|
var extrema = [-1, -1];
|
|
function swapExtrema() {
|
var tmp = extrema[0];
|
extrema[0] = extrema[1];
|
extrema[1] = tmp;
|
}
|
|
function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
|
// Quick reject
|
if (
|
(y > y0 && y > y1 && y > y2 && y > y3)
|
|| (y < y0 && y < y1 && y < y2 && y < y3)
|
) {
|
return 0;
|
}
|
var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots);
|
if (nRoots === 0) {
|
return 0;
|
}
|
else {
|
var w = 0;
|
var nExtrema = -1;
|
var y0_, y1_;
|
for (var i = 0; i < nRoots; i++) {
|
var t = roots[i];
|
|
// Avoid winding error when intersection point is the connect point of two line of polygon
|
var unit = (t === 0 || t === 1) ? 0.5 : 1;
|
|
var x_ = curve.cubicAt(x0, x1, x2, x3, t);
|
if (x_ < x) { // Quick reject
|
continue;
|
}
|
if (nExtrema < 0) {
|
nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema);
|
if (extrema[1] < extrema[0] && nExtrema > 1) {
|
swapExtrema();
|
}
|
y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]);
|
if (nExtrema > 1) {
|
y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]);
|
}
|
}
|
if (nExtrema == 2) {
|
// 分成三段单调函数
|
if (t < extrema[0]) {
|
w += y0_ < y0 ? unit : -unit;
|
}
|
else if (t < extrema[1]) {
|
w += y1_ < y0_ ? unit : -unit;
|
}
|
else {
|
w += y3 < y1_ ? unit : -unit;
|
}
|
}
|
else {
|
// 分成两段单调函数
|
if (t < extrema[0]) {
|
w += y0_ < y0 ? unit : -unit;
|
}
|
else {
|
w += y3 < y0_ ? unit : -unit;
|
}
|
}
|
}
|
return w;
|
}
|
}
|
|
function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
|
// Quick reject
|
if (
|
(y > y0 && y > y1 && y > y2)
|
|| (y < y0 && y < y1 && y < y2)
|
) {
|
return 0;
|
}
|
var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots);
|
if (nRoots === 0) {
|
return 0;
|
}
|
else {
|
var t = curve.quadraticExtremum(y0, y1, y2);
|
if (t >= 0 && t <= 1) {
|
var w = 0;
|
var y_ = curve.quadraticAt(y0, y1, y2, t);
|
for (var i = 0; i < nRoots; i++) {
|
// Remove one endpoint.
|
var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
|
|
var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
|
if (x_ < x) { // Quick reject
|
continue;
|
}
|
if (roots[i] < t) {
|
w += y_ < y0 ? unit : -unit;
|
}
|
else {
|
w += y2 < y_ ? unit : -unit;
|
}
|
}
|
return w;
|
}
|
else {
|
// Remove one endpoint.
|
var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
|
|
var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
|
if (x_ < x) { // Quick reject
|
return 0;
|
}
|
return y2 < y0 ? unit : -unit;
|
}
|
}
|
}
|
|
// TODO
|
// Arc 旋转
|
function windingArc(
|
cx, cy, r, startAngle, endAngle, anticlockwise, x, y
|
) {
|
y -= cy;
|
if (y > r || y < -r) {
|
return 0;
|
}
|
var tmp = Math.sqrt(r * r - y * y);
|
roots[0] = -tmp;
|
roots[1] = tmp;
|
|
var diff = Math.abs(startAngle - endAngle);
|
if (diff < 1e-4) {
|
return 0;
|
}
|
if (diff % PI2 < 1e-4) {
|
// Is a circle
|
startAngle = 0;
|
endAngle = PI2;
|
var dir = anticlockwise ? 1 : -1;
|
if (x >= roots[0] + cx && x <= roots[1] + cx) {
|
return dir;
|
} else {
|
return 0;
|
}
|
}
|
|
if (anticlockwise) {
|
var tmp = startAngle;
|
startAngle = normalizeRadian(endAngle);
|
endAngle = normalizeRadian(tmp);
|
}
|
else {
|
startAngle = normalizeRadian(startAngle);
|
endAngle = normalizeRadian(endAngle);
|
}
|
if (startAngle > endAngle) {
|
endAngle += PI2;
|
}
|
|
var w = 0;
|
for (var i = 0; i < 2; i++) {
|
var x_ = roots[i];
|
if (x_ + cx > x) {
|
var angle = Math.atan2(y, x_);
|
var dir = anticlockwise ? 1 : -1;
|
if (angle < 0) {
|
angle = PI2 + angle;
|
}
|
if (
|
(angle >= startAngle && angle <= endAngle)
|
|| (angle + PI2 >= startAngle && angle + PI2 <= endAngle)
|
) {
|
if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
|
dir = -dir;
|
}
|
w += dir;
|
}
|
}
|
}
|
return w;
|
}
|
|
function containPath(data, lineWidth, isStroke, x, y) {
|
var w = 0;
|
var xi = 0;
|
var yi = 0;
|
var x0 = 0;
|
var y0 = 0;
|
|
for (var i = 0; i < data.length;) {
|
var cmd = data[i++];
|
// Begin a new subpath
|
if (cmd === CMD.M && i > 1) {
|
// Close previous subpath
|
if (!isStroke) {
|
w += windingLine(xi, yi, x0, y0, x, y);
|
}
|
// 如果被任何一个 subpath 包含
|
// if (w !== 0) {
|
// return true;
|
// }
|
}
|
|
if (i == 1) {
|
// 如果第一个命令是 L, C, Q
|
// 则 previous point 同绘制命令的第一个 point
|
//
|
// 第一个命令为 Arc 的情况下会在后面特殊处理
|
xi = data[i];
|
yi = data[i + 1];
|
|
x0 = xi;
|
y0 = yi;
|
}
|
|
switch (cmd) {
|
case CMD.M:
|
// moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
|
// 在 closePath 的时候使用
|
x0 = data[i++];
|
y0 = data[i++];
|
xi = x0;
|
yi = y0;
|
break;
|
case CMD.L:
|
if (isStroke) {
|
if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
|
return true;
|
}
|
}
|
else {
|
// NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN
|
w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
|
}
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.C:
|
if (isStroke) {
|
if (cubic.containStroke(xi, yi,
|
data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
|
lineWidth, x, y
|
)) {
|
return true;
|
}
|
}
|
else {
|
w += windingCubic(
|
xi, yi,
|
data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1],
|
x, y
|
) || 0;
|
}
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.Q:
|
if (isStroke) {
|
if (quadratic.containStroke(xi, yi,
|
data[i++], data[i++], data[i], data[i + 1],
|
lineWidth, x, y
|
)) {
|
return true;
|
}
|
}
|
else {
|
w += windingQuadratic(
|
xi, yi,
|
data[i++], data[i++], data[i], data[i + 1],
|
x, y
|
) || 0;
|
}
|
xi = data[i++];
|
yi = data[i++];
|
break;
|
case CMD.A:
|
// TODO Arc 判断的开销比较大
|
var cx = data[i++];
|
var cy = data[i++];
|
var rx = data[i++];
|
var ry = data[i++];
|
var theta = data[i++];
|
var dTheta = data[i++];
|
// TODO Arc 旋转
|
var psi = data[i++];
|
var anticlockwise = 1 - data[i++];
|
var x1 = Math.cos(theta) * rx + cx;
|
var y1 = Math.sin(theta) * ry + cy;
|
// 不是直接使用 arc 命令
|
if (i > 1) {
|
w += windingLine(xi, yi, x1, y1, x, y);
|
}
|
else {
|
// 第一个命令起点还未定义
|
x0 = x1;
|
y0 = y1;
|
}
|
// zr 使用scale来模拟椭圆, 这里也对x做一定的缩放
|
var _x = (x - cx) * ry / rx + cx;
|
if (isStroke) {
|
if (arc.containStroke(
|
cx, cy, ry, theta, theta + dTheta, anticlockwise,
|
lineWidth, _x, y
|
)) {
|
return true;
|
}
|
}
|
else {
|
w += windingArc(
|
cx, cy, ry, theta, theta + dTheta, anticlockwise,
|
_x, y
|
);
|
}
|
xi = Math.cos(theta + dTheta) * rx + cx;
|
yi = Math.sin(theta + dTheta) * ry + cy;
|
break;
|
case CMD.R:
|
x0 = xi = data[i++];
|
y0 = yi = data[i++];
|
var width = data[i++];
|
var height = data[i++];
|
var x1 = x0 + width;
|
var y1 = y0 + height;
|
if (isStroke) {
|
if (containStroke(x0, y0, x1, y0, lineWidth, x, y)
|
|| containStroke(x1, y0, x1, y1, lineWidth, x, y)
|
|| containStroke(x1, y1, x0, y1, lineWidth, x, y)
|
|| containStroke(x0, y1, x0, y0, lineWidth, x, y)
|
) {
|
return true;
|
}
|
}
|
else {
|
// FIXME Clockwise ?
|
w += windingLine(x1, y0, x1, y1, x, y);
|
w += windingLine(x0, y1, x0, y0, x, y);
|
}
|
break;
|
case CMD.Z:
|
if (isStroke) {
|
if (containStroke(
|
xi, yi, x0, y0, lineWidth, x, y
|
)) {
|
return true;
|
}
|
}
|
else {
|
// Close a subpath
|
w += windingLine(xi, yi, x0, y0, x, y);
|
// 如果被任何一个 subpath 包含
|
// FIXME subpaths may overlap
|
// if (w !== 0) {
|
// return true;
|
// }
|
}
|
xi = x0;
|
yi = y0;
|
break;
|
}
|
}
|
if (!isStroke && !isAroundEqual(yi, y0)) {
|
w += windingLine(xi, yi, x0, y0, x, y) || 0;
|
}
|
return w !== 0;
|
}
|
|
module.exports = {
|
contain: function (pathData, x, y) {
|
return containPath(pathData, 0, false, x, y);
|
},
|
|
containStroke: function (pathData, lineWidth, x, y) {
|
return containPath(pathData, lineWidth, true, x, y);
|
}
|
};
|
|
|
/***/ },
|
/* 54 */
|
/***/ function(module, exports) {
|
|
|
module.exports = {
|
/**
|
* 线段包含判断
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} lineWidth
|
* @param {number} x
|
* @param {number} y
|
* @return {boolean}
|
*/
|
containStroke: function (x0, y0, x1, y1, lineWidth, x, y) {
|
if (lineWidth === 0) {
|
return false;
|
}
|
var _l = lineWidth;
|
var _a = 0;
|
var _b = x0;
|
// Quick reject
|
if (
|
(y > y0 + _l && y > y1 + _l)
|
|| (y < y0 - _l && y < y1 - _l)
|
|| (x > x0 + _l && x > x1 + _l)
|
|| (x < x0 - _l && x < x1 - _l)
|
) {
|
return false;
|
}
|
|
if (x0 !== x1) {
|
_a = (y0 - y1) / (x0 - x1);
|
_b = (x0 * y1 - x1 * y0) / (x0 - x1) ;
|
}
|
else {
|
return Math.abs(x - x0) <= _l / 2;
|
}
|
var tmp = _a * x - y + _b;
|
var _s = tmp * tmp / (_a * _a + 1);
|
return _s <= _l / 2 * _l / 2;
|
}
|
};
|
|
|
/***/ },
|
/* 55 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var curve = __webpack_require__(51);
|
|
module.exports = {
|
/**
|
* 三次贝塞尔曲线描边包含判断
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} x3
|
* @param {number} y3
|
* @param {number} lineWidth
|
* @param {number} x
|
* @param {number} y
|
* @return {boolean}
|
*/
|
containStroke: function(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) {
|
if (lineWidth === 0) {
|
return false;
|
}
|
var _l = lineWidth;
|
// Quick reject
|
if (
|
(y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l)
|
|| (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l)
|
|| (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l)
|
|| (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)
|
) {
|
return false;
|
}
|
var d = curve.cubicProjectPoint(
|
x0, y0, x1, y1, x2, y2, x3, y3,
|
x, y, null
|
);
|
return d <= _l / 2;
|
}
|
};
|
|
|
/***/ },
|
/* 56 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var curve = __webpack_require__(51);
|
|
module.exports = {
|
/**
|
* 二次贝塞尔曲线描边包含判断
|
* @param {number} x0
|
* @param {number} y0
|
* @param {number} x1
|
* @param {number} y1
|
* @param {number} x2
|
* @param {number} y2
|
* @param {number} lineWidth
|
* @param {number} x
|
* @param {number} y
|
* @return {boolean}
|
*/
|
containStroke: function (x0, y0, x1, y1, x2, y2, lineWidth, x, y) {
|
if (lineWidth === 0) {
|
return false;
|
}
|
var _l = lineWidth;
|
// Quick reject
|
if (
|
(y > y0 + _l && y > y1 + _l && y > y2 + _l)
|
|| (y < y0 - _l && y < y1 - _l && y < y2 - _l)
|
|| (x > x0 + _l && x > x1 + _l && x > x2 + _l)
|
|| (x < x0 - _l && x < x1 - _l && x < x2 - _l)
|
) {
|
return false;
|
}
|
var d = curve.quadraticProjectPoint(
|
x0, y0, x1, y1, x2, y2,
|
x, y, null
|
);
|
return d <= _l / 2;
|
}
|
};
|
|
|
/***/ },
|
/* 57 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var normalizeRadian = __webpack_require__(58).normalizeRadian;
|
var PI2 = Math.PI * 2;
|
|
module.exports = {
|
/**
|
* 圆弧描边包含判断
|
* @param {number} cx
|
* @param {number} cy
|
* @param {number} r
|
* @param {number} startAngle
|
* @param {number} endAngle
|
* @param {boolean} anticlockwise
|
* @param {number} lineWidth
|
* @param {number} x
|
* @param {number} y
|
* @return {Boolean}
|
*/
|
containStroke: function (
|
cx, cy, r, startAngle, endAngle, anticlockwise,
|
lineWidth, x, y
|
) {
|
|
if (lineWidth === 0) {
|
return false;
|
}
|
var _l = lineWidth;
|
|
x -= cx;
|
y -= cy;
|
var d = Math.sqrt(x * x + y * y);
|
|
if ((d - _l > r) || (d + _l < r)) {
|
return false;
|
}
|
if (Math.abs(startAngle - endAngle) % PI2 < 1e-4) {
|
// Is a circle
|
return true;
|
}
|
if (anticlockwise) {
|
var tmp = startAngle;
|
startAngle = normalizeRadian(endAngle);
|
endAngle = normalizeRadian(tmp);
|
} else {
|
startAngle = normalizeRadian(startAngle);
|
endAngle = normalizeRadian(endAngle);
|
}
|
if (startAngle > endAngle) {
|
endAngle += PI2;
|
}
|
|
var angle = Math.atan2(y, x);
|
if (angle < 0) {
|
angle += PI2;
|
}
|
return (angle >= startAngle && angle <= endAngle)
|
|| (angle + PI2 >= startAngle && angle + PI2 <= endAngle);
|
}
|
};
|
|
|
/***/ },
|
/* 58 */
|
/***/ function(module, exports) {
|
|
|
|
var PI2 = Math.PI * 2;
|
module.exports = {
|
normalizeRadian: function(angle) {
|
angle %= PI2;
|
if (angle < 0) {
|
angle += PI2;
|
}
|
return angle;
|
}
|
};
|
|
|
/***/ },
|
/* 59 */
|
/***/ function(module, exports) {
|
|
|
module.exports = function windingLine(x0, y0, x1, y1, x, y) {
|
if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
|
return 0;
|
}
|
// Ignore horizontal line
|
if (y1 === y0) {
|
return 0;
|
}
|
var dir = y1 < y0 ? 1 : -1;
|
var t = (y - y0) / (y1 - y0);
|
|
// Avoid winding error when intersection point is the connect point of two line of polygon
|
if (t === 1 || t === 0) {
|
dir = y1 < y0 ? 0.5 : -0.5;
|
}
|
|
var x_ = t * (x1 - x0) + x0;
|
|
return x_ > x ? dir : 0;
|
};
|
|
|
/***/ },
|
/* 60 */
|
/***/ function(module, exports) {
|
|
|
|
var Pattern = function (image, repeat) {
|
// Should do nothing more in this constructor. Because gradient can be
|
// declard by `color: {image: ...}`, where this constructor will not be called.
|
|
this.image = image;
|
this.repeat = repeat;
|
|
// Can be cloned
|
this.type = 'pattern';
|
};
|
|
Pattern.prototype.getCanvasPattern = function (ctx) {
|
return ctx.createPattern(this.image, this.repeat || 'repeat');
|
};
|
|
module.exports = Pattern;
|
|
|
/***/ },
|
/* 61 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var CMD = __webpack_require__(50).CMD;
|
var vec2 = __webpack_require__(10);
|
var v2ApplyTransform = vec2.applyTransform;
|
|
var points = [[], [], []];
|
var mathSqrt = Math.sqrt;
|
var mathAtan2 = Math.atan2;
|
function transformPath(path, m) {
|
var data = path.data;
|
var cmd;
|
var nPoint;
|
var i;
|
var j;
|
var k;
|
var p;
|
|
var M = CMD.M;
|
var C = CMD.C;
|
var L = CMD.L;
|
var R = CMD.R;
|
var A = CMD.A;
|
var Q = CMD.Q;
|
|
for (i = 0, j = 0; i < data.length;) {
|
cmd = data[i++];
|
j = i;
|
nPoint = 0;
|
|
switch (cmd) {
|
case M:
|
nPoint = 1;
|
break;
|
case L:
|
nPoint = 1;
|
break;
|
case C:
|
nPoint = 3;
|
break;
|
case Q:
|
nPoint = 2;
|
break;
|
case A:
|
var x = m[4];
|
var y = m[5];
|
var sx = mathSqrt(m[0] * m[0] + m[1] * m[1]);
|
var sy = mathSqrt(m[2] * m[2] + m[3] * m[3]);
|
var angle = mathAtan2(-m[1] / sy, m[0] / sx);
|
// cx
|
data[i] *= sx;
|
data[i++] += x;
|
// cy
|
data[i] *= sy;
|
data[i++] += y;
|
// Scale rx and ry
|
// FIXME Assume psi is 0 here
|
data[i++] *= sx;
|
data[i++] *= sy;
|
|
// Start angle
|
data[i++] += angle;
|
// end angle
|
data[i++] += angle;
|
// FIXME psi
|
i += 2;
|
j = i;
|
break;
|
case R:
|
// x0, y0
|
p[0] = data[i++];
|
p[1] = data[i++];
|
v2ApplyTransform(p, p, m);
|
data[j++] = p[0];
|
data[j++] = p[1];
|
// x1, y1
|
p[0] += data[i++];
|
p[1] += data[i++];
|
v2ApplyTransform(p, p, m);
|
data[j++] = p[0];
|
data[j++] = p[1];
|
}
|
|
for (k = 0; k < nPoint; k++) {
|
var p = points[k];
|
p[0] = data[i++];
|
p[1] = data[i++];
|
|
v2ApplyTransform(p, p, m);
|
// Write back
|
data[j++] = p[0];
|
data[j++] = p[1];
|
}
|
}
|
}
|
|
module.exports = transformPath;
|
|
|
/***/ },
|
/* 62 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Image element
|
* @module zrender/graphic/Image
|
*/
|
|
|
|
var Displayable = __webpack_require__(47);
|
var BoundingRect = __webpack_require__(9);
|
var zrUtil = __webpack_require__(4);
|
|
var LRU = __webpack_require__(40);
|
var globalImageCache = new LRU(50);
|
/**
|
* @alias zrender/graphic/Image
|
* @extends module:zrender/graphic/Displayable
|
* @constructor
|
* @param {Object} opts
|
*/
|
function ZImage(opts) {
|
Displayable.call(this, opts);
|
}
|
|
ZImage.prototype = {
|
|
constructor: ZImage,
|
|
type: 'image',
|
|
brush: function (ctx, prevEl) {
|
var style = this.style;
|
var src = style.image;
|
var image;
|
|
// Must bind each time
|
style.bind(ctx, this, prevEl);
|
// style.image is a url string
|
if (typeof src === 'string') {
|
image = this._image;
|
}
|
// style.image is an HTMLImageElement or HTMLCanvasElement or Canvas
|
else {
|
image = src;
|
}
|
// FIXME Case create many images with src
|
if (!image && src) {
|
// Try get from global image cache
|
var cachedImgObj = globalImageCache.get(src);
|
if (!cachedImgObj) {
|
// Create a new image
|
image = new Image();
|
image.onload = function () {
|
image.onload = null;
|
for (var i = 0; i < cachedImgObj.pending.length; i++) {
|
cachedImgObj.pending[i].dirty();
|
}
|
};
|
cachedImgObj = {
|
image: image,
|
pending: [this]
|
};
|
image.src = src;
|
globalImageCache.put(src, cachedImgObj);
|
this._image = image;
|
return;
|
}
|
else {
|
image = cachedImgObj.image;
|
this._image = image;
|
// Image is not complete finish, add to pending list
|
if (!image.width || !image.height) {
|
cachedImgObj.pending.push(this);
|
return;
|
}
|
}
|
}
|
|
if (image) {
|
// 图片已经加载完成
|
// if (image.nodeName.toUpperCase() == 'IMG') {
|
// if (!image.complete) {
|
// return;
|
// }
|
// }
|
// Else is canvas
|
|
var x = style.x || 0;
|
var y = style.y || 0;
|
// 图片加载失败
|
if (!image.width || !image.height) {
|
return;
|
}
|
var width = style.width;
|
var height = style.height;
|
var aspect = image.width / image.height;
|
if (width == null && height != null) {
|
// Keep image/height ratio
|
width = height * aspect;
|
}
|
else if (height == null && width != null) {
|
height = width / aspect;
|
}
|
else if (width == null && height == null) {
|
width = image.width;
|
height = image.height;
|
}
|
|
// 设置transform
|
this.setTransform(ctx);
|
|
if (style.sWidth && style.sHeight) {
|
var sx = style.sx || 0;
|
var sy = style.sy || 0;
|
ctx.drawImage(
|
image,
|
sx, sy, style.sWidth, style.sHeight,
|
x, y, width, height
|
);
|
}
|
else if (style.sx && style.sy) {
|
var sx = style.sx;
|
var sy = style.sy;
|
var sWidth = width - sx;
|
var sHeight = height - sy;
|
ctx.drawImage(
|
image,
|
sx, sy, sWidth, sHeight,
|
x, y, width, height
|
);
|
}
|
else {
|
ctx.drawImage(image, x, y, width, height);
|
}
|
|
this.restoreTransform(ctx);
|
|
// Draw rect text
|
if (style.text != null) {
|
this.drawRectText(ctx, this.getBoundingRect());
|
}
|
|
}
|
},
|
|
getBoundingRect: function () {
|
var style = this.style;
|
if (! this._rect) {
|
this._rect = new BoundingRect(
|
style.x || 0, style.y || 0, style.width || 0, style.height || 0
|
);
|
}
|
return this._rect;
|
}
|
};
|
|
zrUtil.inherits(ZImage, Displayable);
|
|
module.exports = ZImage;
|
|
|
/***/ },
|
/* 63 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Text element
|
* @module zrender/graphic/Text
|
*
|
* TODO Wrapping
|
*
|
* Text not support gradient
|
*/
|
|
|
|
var Displayable = __webpack_require__(47);
|
var zrUtil = __webpack_require__(4);
|
var textContain = __webpack_require__(8);
|
|
/**
|
* @alias zrender/graphic/Text
|
* @extends module:zrender/graphic/Displayable
|
* @constructor
|
* @param {Object} opts
|
*/
|
var Text = function (opts) {
|
Displayable.call(this, opts);
|
};
|
|
Text.prototype = {
|
|
constructor: Text,
|
|
type: 'text',
|
|
brush: function (ctx, prevEl) {
|
var style = this.style;
|
var x = style.x || 0;
|
var y = style.y || 0;
|
// Convert to string
|
var text = style.text;
|
|
// Convert to string
|
text != null && (text += '');
|
|
// Always bind style
|
style.bind(ctx, this, prevEl);
|
|
if (text) {
|
|
this.setTransform(ctx);
|
|
var textBaseline;
|
var textAlign = style.textAlign;
|
var font = style.textFont || style.font;
|
if (style.textVerticalAlign) {
|
var rect = textContain.getBoundingRect(
|
text, font, style.textAlign, 'top'
|
);
|
// Ignore textBaseline
|
textBaseline = 'middle';
|
switch (style.textVerticalAlign) {
|
case 'middle':
|
y -= rect.height / 2 - rect.lineHeight / 2;
|
break;
|
case 'bottom':
|
y -= rect.height - rect.lineHeight / 2;
|
break;
|
default:
|
y += rect.lineHeight / 2;
|
}
|
}
|
else {
|
textBaseline = style.textBaseline;
|
}
|
|
// TODO Invalid font
|
ctx.font = font || '12px sans-serif';
|
ctx.textAlign = textAlign || 'left';
|
// Use canvas default left textAlign. Giving invalid value will cause state not change
|
if (ctx.textAlign !== textAlign) {
|
ctx.textAlign = 'left';
|
}
|
// FIXME in text contain default is top
|
ctx.textBaseline = textBaseline || 'alphabetic';
|
// Use canvas default alphabetic baseline
|
if (ctx.textBaseline !== textBaseline) {
|
ctx.textBaseline = 'alphabetic';
|
}
|
|
var lineHeight = textContain.measureText('国', ctx.font).width;
|
|
var textLines = text.split('\n');
|
for (var i = 0; i < textLines.length; i++) {
|
// Fill after stroke so the outline will not cover the main part.
|
style.hasStroke() && ctx.strokeText(textLines[i], x, y);
|
style.hasFill() && ctx.fillText(textLines[i], x, y);
|
y += lineHeight;
|
}
|
|
this.restoreTransform(ctx);
|
}
|
},
|
|
getBoundingRect: function () {
|
var style = this.style;
|
if (!this._rect) {
|
var textVerticalAlign = style.textVerticalAlign;
|
var rect = textContain.getBoundingRect(
|
style.text + '', style.textFont || style.font, style.textAlign,
|
textVerticalAlign ? 'top' : style.textBaseline
|
);
|
switch (textVerticalAlign) {
|
case 'middle':
|
rect.y -= rect.height / 2;
|
break;
|
case 'bottom':
|
rect.y -= rect.height;
|
break;
|
}
|
rect.x += style.x || 0;
|
rect.y += style.y || 0;
|
if (style.hasStroke()) {
|
var w = style.lineWidth;
|
rect.x -= w / 2;
|
rect.y -= w / 2;
|
rect.width += w;
|
rect.height += w;
|
}
|
this._rect = rect;
|
}
|
|
return this._rect;
|
}
|
};
|
|
zrUtil.inherits(Text, Displayable);
|
|
module.exports = Text;
|
|
|
/***/ },
|
/* 64 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 圆形
|
* @module zrender/shape/Circle
|
*/
|
|
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'circle',
|
|
shape: {
|
cx: 0,
|
cy: 0,
|
r: 0
|
},
|
|
|
buildPath : function (ctx, shape, inBundle) {
|
// Better stroking in ShapeBundle
|
// Always do it may have performence issue ( fill may be 2x more cost)
|
if (inBundle) {
|
ctx.moveTo(shape.cx + shape.r, shape.cy);
|
}
|
// else {
|
// if (ctx.allocate && !ctx.data.length) {
|
// ctx.allocate(ctx.CMD_MEM_SIZE.A);
|
// }
|
// }
|
// Better stroking in ShapeBundle
|
// ctx.moveTo(shape.cx + shape.r, shape.cy);
|
ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true);
|
}
|
});
|
|
|
|
/***/ },
|
/* 65 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 扇形
|
* @module zrender/graphic/shape/Sector
|
*/
|
|
|
|
var env = __webpack_require__(2);
|
var Path = __webpack_require__(46);
|
|
var shadowTemp = [
|
['shadowBlur', 0],
|
['shadowColor', '#000'],
|
['shadowOffsetX', 0],
|
['shadowOffsetY', 0]
|
];
|
|
module.exports = Path.extend({
|
|
type: 'sector',
|
|
shape: {
|
|
cx: 0,
|
|
cy: 0,
|
|
r0: 0,
|
|
r: 0,
|
|
startAngle: 0,
|
|
endAngle: Math.PI * 2,
|
|
clockwise: true
|
},
|
|
brush: (env.browser.ie && env.browser.version >= 11) // version: '11.0'
|
// Fix weird bug in some version of IE11 (like 11.0.9600.17801),
|
// where exception "unexpected call to method or property access"
|
// might be thrown when calling ctx.fill after a path whose area size
|
// is zero is drawn and ctx.clip() is called and shadowBlur is set.
|
// (e.g.,
|
// ctx.moveTo(10, 10);
|
// ctx.lineTo(20, 10);
|
// ctx.closePath();
|
// ctx.clip();
|
// ctx.shadowBlur = 10;
|
// ...
|
// ctx.fill();
|
// )
|
? function () {
|
var clipPaths = this.__clipPaths;
|
var style = this.style;
|
var modified;
|
|
if (clipPaths) {
|
for (var i = 0; i < clipPaths.length; i++) {
|
var shape = clipPaths[i] && clipPaths[i].shape;
|
if (shape && shape.startAngle === shape.endAngle) {
|
for (var j = 0; j < shadowTemp.length; j++) {
|
shadowTemp[j][2] = style[shadowTemp[j][0]];
|
style[shadowTemp[j][0]] = shadowTemp[j][1];
|
}
|
modified = true;
|
break;
|
}
|
}
|
}
|
|
Path.prototype.brush.apply(this, arguments);
|
|
if (modified) {
|
for (var j = 0; j < shadowTemp.length; j++) {
|
style[shadowTemp[j][0]] = shadowTemp[j][2];
|
}
|
}
|
}
|
: Path.prototype.brush,
|
|
buildPath: function (ctx, shape) {
|
|
var x = shape.cx;
|
var y = shape.cy;
|
var r0 = Math.max(shape.r0 || 0, 0);
|
var r = Math.max(shape.r, 0);
|
var startAngle = shape.startAngle;
|
var endAngle = shape.endAngle;
|
var clockwise = shape.clockwise;
|
|
var unitX = Math.cos(startAngle);
|
var unitY = Math.sin(startAngle);
|
|
ctx.moveTo(unitX * r0 + x, unitY * r0 + y);
|
|
ctx.lineTo(unitX * r + x, unitY * r + y);
|
|
ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
|
|
ctx.lineTo(
|
Math.cos(endAngle) * r0 + x,
|
Math.sin(endAngle) * r0 + y
|
);
|
|
if (r0 !== 0) {
|
ctx.arc(x, y, r0, endAngle, startAngle, clockwise);
|
}
|
|
ctx.closePath();
|
}
|
});
|
|
|
|
/***/ },
|
/* 66 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 圆环
|
* @module zrender/graphic/shape/Ring
|
*/
|
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'ring',
|
|
shape: {
|
cx: 0,
|
cy: 0,
|
r: 0,
|
r0: 0
|
},
|
|
buildPath: function (ctx, shape) {
|
var x = shape.cx;
|
var y = shape.cy;
|
var PI2 = Math.PI * 2;
|
ctx.moveTo(x + shape.r, y);
|
ctx.arc(x, y, shape.r, 0, PI2, false);
|
ctx.moveTo(x + shape.r0, y);
|
ctx.arc(x, y, shape.r0, 0, PI2, true);
|
}
|
});
|
|
|
|
/***/ },
|
/* 67 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 多边形
|
* @module zrender/shape/Polygon
|
*/
|
|
|
var polyHelper = __webpack_require__(68);
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'polygon',
|
|
shape: {
|
points: null,
|
|
smooth: false,
|
|
smoothConstraint: null
|
},
|
|
buildPath: function (ctx, shape) {
|
polyHelper.buildPath(ctx, shape, true);
|
}
|
});
|
|
|
/***/ },
|
/* 68 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var smoothSpline = __webpack_require__(69);
|
var smoothBezier = __webpack_require__(70);
|
|
module.exports = {
|
buildPath: function (ctx, shape, closePath) {
|
var points = shape.points;
|
var smooth = shape.smooth;
|
if (points && points.length >= 2) {
|
if (smooth && smooth !== 'spline') {
|
var controlPoints = smoothBezier(
|
points, smooth, closePath, shape.smoothConstraint
|
);
|
|
ctx.moveTo(points[0][0], points[0][1]);
|
var len = points.length;
|
for (var i = 0; i < (closePath ? len : len - 1); i++) {
|
var cp1 = controlPoints[i * 2];
|
var cp2 = controlPoints[i * 2 + 1];
|
var p = points[(i + 1) % len];
|
ctx.bezierCurveTo(
|
cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]
|
);
|
}
|
}
|
else {
|
if (smooth === 'spline') {
|
points = smoothSpline(points, closePath);
|
}
|
|
ctx.moveTo(points[0][0], points[0][1]);
|
for (var i = 1, l = points.length; i < l; i++) {
|
ctx.lineTo(points[i][0], points[i][1]);
|
}
|
}
|
|
closePath && ctx.closePath();
|
}
|
}
|
};
|
|
|
/***/ },
|
/* 69 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Catmull-Rom spline 插值折线
|
* @module zrender/shape/util/smoothSpline
|
* @author pissang (https://www.github.com/pissang)
|
* Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* errorrik (errorrik@gmail.com)
|
*/
|
|
var vec2 = __webpack_require__(10);
|
|
/**
|
* @inner
|
*/
|
function interpolate(p0, p1, p2, p3, t, t2, t3) {
|
var v0 = (p2 - p0) * 0.5;
|
var v1 = (p3 - p1) * 0.5;
|
return (2 * (p1 - p2) + v0 + v1) * t3
|
+ (-3 * (p1 - p2) - 2 * v0 - v1) * t2
|
+ v0 * t + p1;
|
}
|
|
/**
|
* @alias module:zrender/shape/util/smoothSpline
|
* @param {Array} points 线段顶点数组
|
* @param {boolean} isLoop
|
* @return {Array}
|
*/
|
module.exports = function (points, isLoop) {
|
var len = points.length;
|
var ret = [];
|
|
var distance = 0;
|
for (var i = 1; i < len; i++) {
|
distance += vec2.distance(points[i - 1], points[i]);
|
}
|
|
var segs = distance / 2;
|
segs = segs < len ? len : segs;
|
for (var i = 0; i < segs; i++) {
|
var pos = i / (segs - 1) * (isLoop ? len : len - 1);
|
var idx = Math.floor(pos);
|
|
var w = pos - idx;
|
|
var p0;
|
var p1 = points[idx % len];
|
var p2;
|
var p3;
|
if (!isLoop) {
|
p0 = points[idx === 0 ? idx : idx - 1];
|
p2 = points[idx > len - 2 ? len - 1 : idx + 1];
|
p3 = points[idx > len - 3 ? len - 1 : idx + 2];
|
}
|
else {
|
p0 = points[(idx - 1 + len) % len];
|
p2 = points[(idx + 1) % len];
|
p3 = points[(idx + 2) % len];
|
}
|
|
var w2 = w * w;
|
var w3 = w * w2;
|
|
ret.push([
|
interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3),
|
interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3)
|
]);
|
}
|
return ret;
|
};
|
|
|
|
/***/ },
|
/* 70 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 贝塞尔平滑曲线
|
* @module zrender/shape/util/smoothBezier
|
* @author pissang (https://www.github.com/pissang)
|
* Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* errorrik (errorrik@gmail.com)
|
*/
|
|
|
var vec2 = __webpack_require__(10);
|
var v2Min = vec2.min;
|
var v2Max = vec2.max;
|
var v2Scale = vec2.scale;
|
var v2Distance = vec2.distance;
|
var v2Add = vec2.add;
|
|
/**
|
* 贝塞尔平滑曲线
|
* @alias module:zrender/shape/util/smoothBezier
|
* @param {Array} points 线段顶点数组
|
* @param {number} smooth 平滑等级, 0-1
|
* @param {boolean} isLoop
|
* @param {Array} constraint 将计算出来的控制点约束在一个包围盒内
|
* 比如 [[0, 0], [100, 100]], 这个包围盒会与
|
* 整个折线的包围盒做一个并集用来约束控制点。
|
* @param {Array} 计算出来的控制点数组
|
*/
|
module.exports = function (points, smooth, isLoop, constraint) {
|
var cps = [];
|
|
var v = [];
|
var v1 = [];
|
var v2 = [];
|
var prevPoint;
|
var nextPoint;
|
|
var min, max;
|
if (constraint) {
|
min = [Infinity, Infinity];
|
max = [-Infinity, -Infinity];
|
for (var i = 0, len = points.length; i < len; i++) {
|
v2Min(min, min, points[i]);
|
v2Max(max, max, points[i]);
|
}
|
// 与指定的包围盒做并集
|
v2Min(min, min, constraint[0]);
|
v2Max(max, max, constraint[1]);
|
}
|
|
for (var i = 0, len = points.length; i < len; i++) {
|
var point = points[i];
|
|
if (isLoop) {
|
prevPoint = points[i ? i - 1 : len - 1];
|
nextPoint = points[(i + 1) % len];
|
}
|
else {
|
if (i === 0 || i === len - 1) {
|
cps.push(vec2.clone(points[i]));
|
continue;
|
}
|
else {
|
prevPoint = points[i - 1];
|
nextPoint = points[i + 1];
|
}
|
}
|
|
vec2.sub(v, nextPoint, prevPoint);
|
|
// use degree to scale the handle length
|
v2Scale(v, v, smooth);
|
|
var d0 = v2Distance(point, prevPoint);
|
var d1 = v2Distance(point, nextPoint);
|
var sum = d0 + d1;
|
if (sum !== 0) {
|
d0 /= sum;
|
d1 /= sum;
|
}
|
|
v2Scale(v1, v, -d0);
|
v2Scale(v2, v, d1);
|
var cp0 = v2Add([], point, v1);
|
var cp1 = v2Add([], point, v2);
|
if (constraint) {
|
v2Max(cp0, cp0, min);
|
v2Min(cp0, cp0, max);
|
v2Max(cp1, cp1, min);
|
v2Min(cp1, cp1, max);
|
}
|
cps.push(cp0);
|
cps.push(cp1);
|
}
|
|
if (isLoop) {
|
cps.push(cps.shift());
|
}
|
|
return cps;
|
};
|
|
|
|
/***/ },
|
/* 71 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module zrender/graphic/shape/Polyline
|
*/
|
|
|
var polyHelper = __webpack_require__(68);
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'polyline',
|
|
shape: {
|
points: null,
|
|
smooth: false,
|
|
smoothConstraint: null
|
},
|
|
style: {
|
stroke: '#000',
|
|
fill: null
|
},
|
|
buildPath: function (ctx, shape) {
|
polyHelper.buildPath(ctx, shape, false);
|
}
|
});
|
|
|
/***/ },
|
/* 72 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 矩形
|
* @module zrender/graphic/shape/Rect
|
*/
|
|
|
var roundRectHelper = __webpack_require__(73);
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'rect',
|
|
shape: {
|
// 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4
|
// r缩写为1 相当于 [1, 1, 1, 1]
|
// r缩写为[1] 相当于 [1, 1, 1, 1]
|
// r缩写为[1, 2] 相当于 [1, 2, 1, 2]
|
// r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2]
|
r: 0,
|
|
x: 0,
|
y: 0,
|
width: 0,
|
height: 0
|
},
|
|
buildPath: function (ctx, shape) {
|
var x = shape.x;
|
var y = shape.y;
|
var width = shape.width;
|
var height = shape.height;
|
if (!shape.r) {
|
ctx.rect(x, y, width, height);
|
}
|
else {
|
roundRectHelper.buildPath(ctx, shape);
|
}
|
ctx.closePath();
|
return;
|
}
|
});
|
|
|
|
/***/ },
|
/* 73 */
|
/***/ function(module, exports) {
|
|
|
|
module.exports = {
|
buildPath: function (ctx, shape) {
|
var x = shape.x;
|
var y = shape.y;
|
var width = shape.width;
|
var height = shape.height;
|
var r = shape.r;
|
var r1;
|
var r2;
|
var r3;
|
var r4;
|
|
// Convert width and height to positive for better borderRadius
|
if (width < 0) {
|
x = x + width;
|
width = -width;
|
}
|
if (height < 0) {
|
y = y + height;
|
height = -height;
|
}
|
|
if (typeof r === 'number') {
|
r1 = r2 = r3 = r4 = r;
|
}
|
else if (r instanceof Array) {
|
if (r.length === 1) {
|
r1 = r2 = r3 = r4 = r[0];
|
}
|
else if (r.length === 2) {
|
r1 = r3 = r[0];
|
r2 = r4 = r[1];
|
}
|
else if (r.length === 3) {
|
r1 = r[0];
|
r2 = r4 = r[1];
|
r3 = r[2];
|
}
|
else {
|
r1 = r[0];
|
r2 = r[1];
|
r3 = r[2];
|
r4 = r[3];
|
}
|
}
|
else {
|
r1 = r2 = r3 = r4 = 0;
|
}
|
|
var total;
|
if (r1 + r2 > width) {
|
total = r1 + r2;
|
r1 *= width / total;
|
r2 *= width / total;
|
}
|
if (r3 + r4 > width) {
|
total = r3 + r4;
|
r3 *= width / total;
|
r4 *= width / total;
|
}
|
if (r2 + r3 > height) {
|
total = r2 + r3;
|
r2 *= height / total;
|
r3 *= height / total;
|
}
|
if (r1 + r4 > height) {
|
total = r1 + r4;
|
r1 *= height / total;
|
r4 *= height / total;
|
}
|
ctx.moveTo(x + r1, y);
|
ctx.lineTo(x + width - r2, y);
|
r2 !== 0 && ctx.quadraticCurveTo(
|
x + width, y, x + width, y + r2
|
);
|
ctx.lineTo(x + width, y + height - r3);
|
r3 !== 0 && ctx.quadraticCurveTo(
|
x + width, y + height, x + width - r3, y + height
|
);
|
ctx.lineTo(x + r4, y + height);
|
r4 !== 0 && ctx.quadraticCurveTo(
|
x, y + height, x, y + height - r4
|
);
|
ctx.lineTo(x, y + r1);
|
r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y);
|
}
|
};
|
|
|
/***/ },
|
/* 74 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 直线
|
* @module zrender/graphic/shape/Line
|
*/
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'line',
|
|
shape: {
|
// Start point
|
x1: 0,
|
y1: 0,
|
// End point
|
x2: 0,
|
y2: 0,
|
|
percent: 1
|
},
|
|
style: {
|
stroke: '#000',
|
fill: null
|
},
|
|
buildPath: function (ctx, shape) {
|
var x1 = shape.x1;
|
var y1 = shape.y1;
|
var x2 = shape.x2;
|
var y2 = shape.y2;
|
var percent = shape.percent;
|
|
if (percent === 0) {
|
return;
|
}
|
|
ctx.moveTo(x1, y1);
|
|
if (percent < 1) {
|
x2 = x1 * (1 - percent) + x2 * percent;
|
y2 = y1 * (1 - percent) + y2 * percent;
|
}
|
ctx.lineTo(x2, y2);
|
},
|
|
/**
|
* Get point at percent
|
* @param {number} percent
|
* @return {Array.<number>}
|
*/
|
pointAt: function (p) {
|
var shape = this.shape;
|
return [
|
shape.x1 * (1 - p) + shape.x2 * p,
|
shape.y1 * (1 - p) + shape.y2 * p
|
];
|
}
|
});
|
|
|
|
/***/ },
|
/* 75 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 贝塞尔曲线
|
* @module zrender/shape/BezierCurve
|
*/
|
|
|
var curveTool = __webpack_require__(51);
|
var vec2 = __webpack_require__(10);
|
var quadraticSubdivide = curveTool.quadraticSubdivide;
|
var cubicSubdivide = curveTool.cubicSubdivide;
|
var quadraticAt = curveTool.quadraticAt;
|
var cubicAt = curveTool.cubicAt;
|
var quadraticDerivativeAt = curveTool.quadraticDerivativeAt;
|
var cubicDerivativeAt = curveTool.cubicDerivativeAt;
|
|
var out = [];
|
|
function someVectorAt(shape, t, isTangent) {
|
var cpx2 = shape.cpx2;
|
var cpy2 = shape.cpy2;
|
if (cpx2 === null || cpy2 === null) {
|
return [
|
(isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t),
|
(isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t)
|
];
|
}
|
else {
|
return [
|
(isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t),
|
(isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t)
|
];
|
}
|
}
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'bezier-curve',
|
|
shape: {
|
x1: 0,
|
y1: 0,
|
x2: 0,
|
y2: 0,
|
cpx1: 0,
|
cpy1: 0,
|
// cpx2: 0,
|
// cpy2: 0
|
|
// Curve show percent, for animating
|
percent: 1
|
},
|
|
style: {
|
stroke: '#000',
|
fill: null
|
},
|
|
buildPath: function (ctx, shape) {
|
var x1 = shape.x1;
|
var y1 = shape.y1;
|
var x2 = shape.x2;
|
var y2 = shape.y2;
|
var cpx1 = shape.cpx1;
|
var cpy1 = shape.cpy1;
|
var cpx2 = shape.cpx2;
|
var cpy2 = shape.cpy2;
|
var percent = shape.percent;
|
if (percent === 0) {
|
return;
|
}
|
|
ctx.moveTo(x1, y1);
|
|
if (cpx2 == null || cpy2 == null) {
|
if (percent < 1) {
|
quadraticSubdivide(
|
x1, cpx1, x2, percent, out
|
);
|
cpx1 = out[1];
|
x2 = out[2];
|
quadraticSubdivide(
|
y1, cpy1, y2, percent, out
|
);
|
cpy1 = out[1];
|
y2 = out[2];
|
}
|
|
ctx.quadraticCurveTo(
|
cpx1, cpy1,
|
x2, y2
|
);
|
}
|
else {
|
if (percent < 1) {
|
cubicSubdivide(
|
x1, cpx1, cpx2, x2, percent, out
|
);
|
cpx1 = out[1];
|
cpx2 = out[2];
|
x2 = out[3];
|
cubicSubdivide(
|
y1, cpy1, cpy2, y2, percent, out
|
);
|
cpy1 = out[1];
|
cpy2 = out[2];
|
y2 = out[3];
|
}
|
ctx.bezierCurveTo(
|
cpx1, cpy1,
|
cpx2, cpy2,
|
x2, y2
|
);
|
}
|
},
|
|
/**
|
* Get point at percent
|
* @param {number} t
|
* @return {Array.<number>}
|
*/
|
pointAt: function (t) {
|
return someVectorAt(this.shape, t, false);
|
},
|
|
/**
|
* Get tangent at percent
|
* @param {number} t
|
* @return {Array.<number>}
|
*/
|
tangentAt: function (t) {
|
var p = someVectorAt(this.shape, t, true);
|
return vec2.normalize(p, p);
|
}
|
});
|
|
|
|
/***/ },
|
/* 76 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* 圆弧
|
* @module zrender/graphic/shape/Arc
|
*/
|
|
|
module.exports = __webpack_require__(46).extend({
|
|
type: 'arc',
|
|
shape: {
|
|
cx: 0,
|
|
cy: 0,
|
|
r: 0,
|
|
startAngle: 0,
|
|
endAngle: Math.PI * 2,
|
|
clockwise: true
|
},
|
|
style: {
|
|
stroke: '#000',
|
|
fill: null
|
},
|
|
buildPath: function (ctx, shape) {
|
|
var x = shape.cx;
|
var y = shape.cy;
|
var r = Math.max(shape.r, 0);
|
var startAngle = shape.startAngle;
|
var endAngle = shape.endAngle;
|
var clockwise = shape.clockwise;
|
|
var unitX = Math.cos(startAngle);
|
var unitY = Math.sin(startAngle);
|
|
ctx.moveTo(unitX * r + x, unitY * r + y);
|
ctx.arc(x, y, r, startAngle, endAngle, !clockwise);
|
}
|
});
|
|
|
/***/ },
|
/* 77 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// CompoundPath to improve performance
|
|
|
var Path = __webpack_require__(46);
|
|
module.exports = Path.extend({
|
|
type: 'compound',
|
|
shape: {
|
|
paths: null
|
},
|
|
_updatePathDirty: function () {
|
var dirtyPath = this.__dirtyPath;
|
var paths = this.shape.paths;
|
for (var i = 0; i < paths.length; i++) {
|
// Mark as dirty if any subpath is dirty
|
dirtyPath = dirtyPath || paths[i].__dirtyPath;
|
}
|
this.__dirtyPath = dirtyPath;
|
this.__dirty = this.__dirty || dirtyPath;
|
},
|
|
beforeBrush: function () {
|
this._updatePathDirty();
|
var paths = this.shape.paths || [];
|
var scale = this.getGlobalScale();
|
// Update path scale
|
for (var i = 0; i < paths.length; i++) {
|
if (!paths[i].path) {
|
paths[i].createPathProxy();
|
}
|
paths[i].path.setScale(scale[0], scale[1]);
|
}
|
},
|
|
buildPath: function (ctx, shape) {
|
var paths = shape.paths || [];
|
for (var i = 0; i < paths.length; i++) {
|
paths[i].buildPath(ctx, paths[i].shape, true);
|
}
|
},
|
|
afterBrush: function () {
|
var paths = this.shape.paths;
|
for (var i = 0; i < paths.length; i++) {
|
paths[i].__dirtyPath = false;
|
}
|
},
|
|
getBoundingRect: function () {
|
this._updatePathDirty();
|
return Path.prototype.getBoundingRect.call(this);
|
}
|
});
|
|
|
/***/ },
|
/* 78 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var Gradient = __webpack_require__(79);
|
|
/**
|
* x, y, x2, y2 are all percent from 0 to 1
|
* @param {number} [x=0]
|
* @param {number} [y=0]
|
* @param {number} [x2=1]
|
* @param {number} [y2=0]
|
* @param {Array.<Object>} colorStops
|
* @param {boolean} [globalCoord=false]
|
*/
|
var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) {
|
// Should do nothing more in this constructor. Because gradient can be
|
// declard by `color: {type: 'linear', colorStops: ...}`, where
|
// this constructor will not be called.
|
|
this.x = x == null ? 0 : x;
|
|
this.y = y == null ? 0 : y;
|
|
this.x2 = x2 == null ? 1 : x2;
|
|
this.y2 = y2 == null ? 0 : y2;
|
|
// Can be cloned
|
this.type = 'linear';
|
|
// If use global coord
|
this.global = globalCoord || false;
|
|
Gradient.call(this, colorStops);
|
};
|
|
LinearGradient.prototype = {
|
|
constructor: LinearGradient
|
};
|
|
zrUtil.inherits(LinearGradient, Gradient);
|
|
module.exports = LinearGradient;
|
|
|
/***/ },
|
/* 79 */
|
/***/ function(module, exports) {
|
|
|
|
/**
|
* @param {Array.<Object>} colorStops
|
*/
|
var Gradient = function (colorStops) {
|
|
this.colorStops = colorStops || [];
|
};
|
|
Gradient.prototype = {
|
|
constructor: Gradient,
|
|
addColorStop: function (offset, color) {
|
this.colorStops.push({
|
|
offset: offset,
|
|
color: color
|
});
|
}
|
};
|
|
module.exports = Gradient;
|
|
|
/***/ },
|
/* 80 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var Gradient = __webpack_require__(79);
|
|
/**
|
* x, y, r are all percent from 0 to 1
|
* @param {number} [x=0.5]
|
* @param {number} [y=0.5]
|
* @param {number} [r=0.5]
|
* @param {Array.<Object>} [colorStops]
|
* @param {boolean} [globalCoord=false]
|
*/
|
var RadialGradient = function (x, y, r, colorStops, globalCoord) {
|
// Should do nothing more in this constructor. Because gradient can be
|
// declard by `color: {type: 'radial', colorStops: ...}`, where
|
// this constructor will not be called.
|
|
this.x = x == null ? 0.5 : x;
|
|
this.y = y == null ? 0.5 : y;
|
|
this.r = r == null ? 0.5 : r;
|
|
// Can be cloned
|
this.type = 'radial';
|
|
// If use global coord
|
this.global = globalCoord || false;
|
|
Gradient.call(this, colorStops);
|
};
|
|
RadialGradient.prototype = {
|
|
constructor: RadialGradient
|
};
|
|
zrUtil.inherits(RadialGradient, Gradient);
|
|
module.exports = RadialGradient;
|
|
|
/***/ },
|
/* 81 */
|
/***/ function(module, exports) {
|
|
|
|
var lib = {};
|
|
var ORIGIN_METHOD = '\0__throttleOriginMethod';
|
var RATE = '\0__throttleRate';
|
var THROTTLE_TYPE = '\0__throttleType';
|
|
/**
|
* @public
|
* @param {(Function)} fn
|
* @param {number} [delay=0] Unit: ms.
|
* @param {boolean} [debounce=false]
|
* true: If call interval less than `delay`, only the last call works.
|
* false: If call interval less than `delay, call works on fixed rate.
|
* @return {(Function)} throttled fn.
|
*/
|
lib.throttle = function (fn, delay, debounce) {
|
|
var currCall;
|
var lastCall = 0;
|
var lastExec = 0;
|
var timer = null;
|
var diff;
|
var scope;
|
var args;
|
var debounceNextCall;
|
|
delay = delay || 0;
|
|
function exec() {
|
lastExec = (new Date()).getTime();
|
timer = null;
|
fn.apply(scope, args || []);
|
}
|
|
var cb = function () {
|
currCall = (new Date()).getTime();
|
scope = this;
|
args = arguments;
|
var thisDelay = debounceNextCall || delay;
|
var thisDebounce = debounceNextCall || debounce;
|
debounceNextCall = null;
|
diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay;
|
|
clearTimeout(timer);
|
|
if (thisDebounce) {
|
timer = setTimeout(exec, thisDelay);
|
}
|
else {
|
if (diff >= 0) {
|
exec();
|
}
|
else {
|
timer = setTimeout(exec, -diff);
|
}
|
}
|
|
lastCall = currCall;
|
};
|
|
/**
|
* Clear throttle.
|
* @public
|
*/
|
cb.clear = function () {
|
if (timer) {
|
clearTimeout(timer);
|
timer = null;
|
}
|
};
|
|
/**
|
* Enable debounce once.
|
*/
|
cb.debounceNextCall = function (debounceDelay) {
|
debounceNextCall = debounceDelay;
|
};
|
|
return cb;
|
};
|
|
/**
|
* Create throttle method or update throttle rate.
|
*
|
* @example
|
* ComponentView.prototype.render = function () {
|
* ...
|
* throttle.createOrUpdate(
|
* this,
|
* '_dispatchAction',
|
* this.model.get('throttle'),
|
* 'fixRate'
|
* );
|
* };
|
* ComponentView.prototype.remove = function () {
|
* throttle.clear(this, '_dispatchAction');
|
* };
|
* ComponentView.prototype.dispose = function () {
|
* throttle.clear(this, '_dispatchAction');
|
* };
|
*
|
* @public
|
* @param {Object} obj
|
* @param {string} fnAttr
|
* @param {number} [rate]
|
* @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce'
|
* @return {Function} throttled function.
|
*/
|
lib.createOrUpdate = function (obj, fnAttr, rate, throttleType) {
|
var fn = obj[fnAttr];
|
|
if (!fn) {
|
return;
|
}
|
|
var originFn = fn[ORIGIN_METHOD] || fn;
|
var lastThrottleType = fn[THROTTLE_TYPE];
|
var lastRate = fn[RATE];
|
|
if (lastRate !== rate || lastThrottleType !== throttleType) {
|
if (rate == null || !throttleType) {
|
return (obj[fnAttr] = originFn);
|
}
|
|
fn = obj[fnAttr] = lib.throttle(
|
originFn, rate, throttleType === 'debounce'
|
);
|
fn[ORIGIN_METHOD] = originFn;
|
fn[THROTTLE_TYPE] = throttleType;
|
fn[RATE] = rate;
|
}
|
|
return fn;
|
};
|
|
/**
|
* Clear throttle. Example see throttle.createOrUpdate.
|
*
|
* @public
|
* @param {Object} obj
|
* @param {string} fnAttr
|
*/
|
lib.clear = function (obj, fnAttr) {
|
var fn = obj[fnAttr];
|
if (fn && fn[ORIGIN_METHOD]) {
|
obj[fnAttr] = fn[ORIGIN_METHOD];
|
}
|
};
|
|
module.exports = lib;
|
|
|
|
/***/ },
|
/* 82 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/*!
|
* ZRender, a high performance 2d drawing library.
|
*
|
* Copyright (c) 2013, Baidu Inc.
|
* All rights reserved.
|
*
|
* LICENSE
|
* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt
|
*/
|
// Global defines
|
|
var guid = __webpack_require__(32);
|
var env = __webpack_require__(2);
|
var zrUtil = __webpack_require__(4);
|
|
var Handler = __webpack_require__(83);
|
var Storage = __webpack_require__(85);
|
var Animation = __webpack_require__(87);
|
var HandlerProxy = __webpack_require__(90);
|
|
var useVML = !env.canvasSupported;
|
|
var painterCtors = {
|
canvas: __webpack_require__(92)
|
};
|
|
var instances = {}; // ZRender实例map索引
|
|
var zrender = {};
|
|
/**
|
* @type {string}
|
*/
|
zrender.version = '3.4.4';
|
|
/**
|
* Initializing a zrender instance
|
* @param {HTMLElement} dom
|
* @param {Object} opts
|
* @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
|
* @param {number} [opts.devicePixelRatio]
|
* @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
|
* @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
|
* @return {module:zrender/ZRender}
|
*/
|
zrender.init = function(dom, opts) {
|
var zr = new ZRender(guid(), dom, opts);
|
instances[zr.id] = zr;
|
return zr;
|
};
|
|
/**
|
* Dispose zrender instance
|
* @param {module:zrender/ZRender} zr
|
*/
|
zrender.dispose = function (zr) {
|
if (zr) {
|
zr.dispose();
|
}
|
else {
|
for (var key in instances) {
|
if (instances.hasOwnProperty(key)) {
|
instances[key].dispose();
|
}
|
}
|
instances = {};
|
}
|
|
return zrender;
|
};
|
|
/**
|
* Get zrender instance by id
|
* @param {string} id zrender instance id
|
* @return {module:zrender/ZRender}
|
*/
|
zrender.getInstance = function (id) {
|
return instances[id];
|
};
|
|
zrender.registerPainter = function (name, Ctor) {
|
painterCtors[name] = Ctor;
|
};
|
|
function delInstance(id) {
|
delete instances[id];
|
}
|
|
/**
|
* @module zrender/ZRender
|
*/
|
/**
|
* @constructor
|
* @alias module:zrender/ZRender
|
* @param {string} id
|
* @param {HTMLDomElement} dom
|
* @param {Object} opts
|
* @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'
|
* @param {number} [opts.devicePixelRatio]
|
* @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
|
* @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
|
*/
|
var ZRender = function(id, dom, opts) {
|
|
opts = opts || {};
|
|
/**
|
* @type {HTMLDomElement}
|
*/
|
this.dom = dom;
|
|
/**
|
* @type {string}
|
*/
|
this.id = id;
|
|
var self = this;
|
var storage = new Storage();
|
|
var rendererType = opts.renderer;
|
// TODO WebGL
|
if (useVML) {
|
if (!painterCtors.vml) {
|
throw new Error('You need to require \'zrender/vml/vml\' to support IE8');
|
}
|
rendererType = 'vml';
|
}
|
else if (!rendererType || !painterCtors[rendererType]) {
|
rendererType = 'canvas';
|
}
|
var painter = new painterCtors[rendererType](dom, storage, opts);
|
|
this.storage = storage;
|
this.painter = painter;
|
|
var handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null;
|
this.handler = new Handler(storage, painter, handerProxy, painter.root);
|
|
/**
|
* @type {module:zrender/animation/Animation}
|
*/
|
this.animation = new Animation({
|
stage: {
|
update: zrUtil.bind(this.flush, this)
|
}
|
});
|
this.animation.start();
|
|
/**
|
* @type {boolean}
|
* @private
|
*/
|
this._needsRefresh;
|
|
// 修改 storage.delFromStorage, 每次删除元素之前删除动画
|
// FIXME 有点ugly
|
var oldDelFromStorage = storage.delFromStorage;
|
var oldAddToStorage = storage.addToStorage;
|
|
storage.delFromStorage = function (el) {
|
oldDelFromStorage.call(storage, el);
|
|
el && el.removeSelfFromZr(self);
|
};
|
|
storage.addToStorage = function (el) {
|
oldAddToStorage.call(storage, el);
|
|
el.addSelfToZr(self);
|
};
|
};
|
|
ZRender.prototype = {
|
|
constructor: ZRender,
|
/**
|
* 获取实例唯一标识
|
* @return {string}
|
*/
|
getId: function () {
|
return this.id;
|
},
|
|
/**
|
* 添加元素
|
* @param {module:zrender/Element} el
|
*/
|
add: function (el) {
|
this.storage.addRoot(el);
|
this._needsRefresh = true;
|
},
|
|
/**
|
* 删除元素
|
* @param {module:zrender/Element} el
|
*/
|
remove: function (el) {
|
this.storage.delRoot(el);
|
this._needsRefresh = true;
|
},
|
|
/**
|
* Change configuration of layer
|
* @param {string} zLevel
|
* @param {Object} config
|
* @param {string} [config.clearColor=0] Clear color
|
* @param {string} [config.motionBlur=false] If enable motion blur
|
* @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer
|
*/
|
configLayer: function (zLevel, config) {
|
this.painter.configLayer(zLevel, config);
|
this._needsRefresh = true;
|
},
|
|
/**
|
* Repaint the canvas immediately
|
*/
|
refreshImmediately: function () {
|
// Clear needsRefresh ahead to avoid something wrong happens in refresh
|
// Or it will cause zrender refreshes again and again.
|
this._needsRefresh = false;
|
this.painter.refresh();
|
/**
|
* Avoid trigger zr.refresh in Element#beforeUpdate hook
|
*/
|
this._needsRefresh = false;
|
},
|
|
/**
|
* Mark and repaint the canvas in the next frame of browser
|
*/
|
refresh: function() {
|
this._needsRefresh = true;
|
},
|
|
/**
|
* Perform all refresh
|
*/
|
flush: function () {
|
if (this._needsRefresh) {
|
this.refreshImmediately();
|
}
|
if (this._needsRefreshHover) {
|
this.refreshHoverImmediately();
|
}
|
},
|
|
/**
|
* Add element to hover layer
|
* @param {module:zrender/Element} el
|
* @param {Object} style
|
*/
|
addHover: function (el, style) {
|
if (this.painter.addHover) {
|
this.painter.addHover(el, style);
|
this.refreshHover();
|
}
|
},
|
|
/**
|
* Add element from hover layer
|
* @param {module:zrender/Element} el
|
*/
|
removeHover: function (el) {
|
if (this.painter.removeHover) {
|
this.painter.removeHover(el);
|
this.refreshHover();
|
}
|
},
|
|
/**
|
* Clear all hover elements in hover layer
|
* @param {module:zrender/Element} el
|
*/
|
clearHover: function () {
|
if (this.painter.clearHover) {
|
this.painter.clearHover();
|
this.refreshHover();
|
}
|
},
|
|
/**
|
* Refresh hover in next frame
|
*/
|
refreshHover: function () {
|
this._needsRefreshHover = true;
|
},
|
|
/**
|
* Refresh hover immediately
|
*/
|
refreshHoverImmediately: function () {
|
this._needsRefreshHover = false;
|
this.painter.refreshHover && this.painter.refreshHover();
|
},
|
|
/**
|
* Resize the canvas.
|
* Should be invoked when container size is changed
|
* @param {Object} [opts]
|
* @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)
|
* @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)
|
*/
|
resize: function(opts) {
|
opts = opts || {};
|
this.painter.resize(opts.width, opts.height);
|
this.handler.resize();
|
},
|
|
/**
|
* Stop and clear all animation immediately
|
*/
|
clearAnimation: function () {
|
this.animation.clear();
|
},
|
|
/**
|
* Get container width
|
*/
|
getWidth: function() {
|
return this.painter.getWidth();
|
},
|
|
/**
|
* Get container height
|
*/
|
getHeight: function() {
|
return this.painter.getHeight();
|
},
|
|
/**
|
* Export the canvas as Base64 URL
|
* @param {string} type
|
* @param {string} [backgroundColor='#fff']
|
* @return {string} Base64 URL
|
*/
|
// toDataURL: function(type, backgroundColor) {
|
// return this.painter.getRenderedCanvas({
|
// backgroundColor: backgroundColor
|
// }).toDataURL(type);
|
// },
|
|
/**
|
* Converting a path to image.
|
* It has much better performance of drawing image rather than drawing a vector path.
|
* @param {module:zrender/graphic/Path} e
|
* @param {number} width
|
* @param {number} height
|
*/
|
pathToImage: function(e, dpr) {
|
return this.painter.pathToImage(e, dpr);
|
},
|
|
/**
|
* Set default cursor
|
* @param {string} [cursorStyle='default'] 例如 crosshair
|
*/
|
setCursorStyle: function (cursorStyle) {
|
this.handler.setCursorStyle(cursorStyle);
|
},
|
|
/**
|
* Find hovered element
|
* @param {number} x
|
* @param {number} y
|
* @return {Object} {target, topTarget}
|
*/
|
findHover: function (x, y) {
|
return this.handler.findHover(x, y);
|
},
|
|
/**
|
* Bind event
|
*
|
* @param {string} eventName Event name
|
* @param {Function} eventHandler Handler function
|
* @param {Object} [context] Context object
|
*/
|
on: function(eventName, eventHandler, context) {
|
this.handler.on(eventName, eventHandler, context);
|
},
|
|
/**
|
* Unbind event
|
* @param {string} eventName Event name
|
* @param {Function} [eventHandler] Handler function
|
*/
|
off: function(eventName, eventHandler) {
|
this.handler.off(eventName, eventHandler);
|
},
|
|
/**
|
* Trigger event manually
|
*
|
* @param {string} eventName Event name
|
* @param {event=} event Event object
|
*/
|
trigger: function (eventName, event) {
|
this.handler.trigger(eventName, event);
|
},
|
|
|
/**
|
* Clear all objects and the canvas.
|
*/
|
clear: function () {
|
this.storage.delRoot();
|
this.painter.clear();
|
},
|
|
/**
|
* Dispose self.
|
*/
|
dispose: function () {
|
this.animation.stop();
|
|
this.clear();
|
this.storage.dispose();
|
this.painter.dispose();
|
this.handler.dispose();
|
|
this.animation =
|
this.storage =
|
this.painter =
|
this.handler = null;
|
|
delInstance(this.id);
|
}
|
};
|
|
module.exports = zrender;
|
|
|
|
/***/ },
|
/* 83 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Handler
|
* @module zrender/Handler
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* errorrik (errorrik@gmail.com)
|
* pissang (shenyi.914@gmail.com)
|
*/
|
|
|
var util = __webpack_require__(4);
|
var Draggable = __webpack_require__(84);
|
|
var Eventful = __webpack_require__(33);
|
|
var SILENT = 'silent';
|
|
function makeEventPacket(eveType, targetInfo, event) {
|
return {
|
type: eveType,
|
event: event,
|
// target can only be an element that is not silent.
|
target: targetInfo.target,
|
// topTarget can be a silent element.
|
topTarget: targetInfo.topTarget,
|
cancelBubble: false,
|
offsetX: event.zrX,
|
offsetY: event.zrY,
|
gestureEvent: event.gestureEvent,
|
pinchX: event.pinchX,
|
pinchY: event.pinchY,
|
pinchScale: event.pinchScale,
|
wheelDelta: event.zrDelta,
|
zrByTouch: event.zrByTouch
|
};
|
}
|
|
function EmptyProxy () {}
|
EmptyProxy.prototype.dispose = function () {};
|
|
var handlerNames = [
|
'click', 'dblclick', 'mousewheel', 'mouseout',
|
'mouseup', 'mousedown', 'mousemove', 'contextmenu'
|
];
|
/**
|
* @alias module:zrender/Handler
|
* @constructor
|
* @extends module:zrender/mixin/Eventful
|
* @param {module:zrender/Storage} storage Storage instance.
|
* @param {module:zrender/Painter} painter Painter instance.
|
* @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.
|
* @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).
|
*/
|
var Handler = function(storage, painter, proxy, painterRoot) {
|
Eventful.call(this);
|
|
this.storage = storage;
|
|
this.painter = painter;
|
|
this.painterRoot = painterRoot;
|
|
proxy = proxy || new EmptyProxy();
|
|
/**
|
* Proxy of event. can be Dom, WebGLSurface, etc.
|
*/
|
this.proxy = proxy;
|
|
// Attach handler
|
proxy.handler = this;
|
|
/**
|
* {target, topTarget}
|
* @private
|
* @type {Object}
|
*/
|
this._hovered = {};
|
|
/**
|
* @private
|
* @type {Date}
|
*/
|
this._lastTouchMoment;
|
|
/**
|
* @private
|
* @type {number}
|
*/
|
this._lastX;
|
|
/**
|
* @private
|
* @type {number}
|
*/
|
this._lastY;
|
|
|
Draggable.call(this);
|
|
util.each(handlerNames, function (name) {
|
proxy.on && proxy.on(name, this[name], this);
|
}, this);
|
};
|
|
Handler.prototype = {
|
|
constructor: Handler,
|
|
mousemove: function (event) {
|
var x = event.zrX;
|
var y = event.zrY;
|
|
var lastHovered = this._hovered;
|
var hovered = this._hovered = this.findHover(x, y);
|
var hoveredTarget = hovered.target;
|
var lastHoveredTarget = lastHovered.target;
|
|
var proxy = this.proxy;
|
proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');
|
|
// Mouse out on previous hovered element
|
if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget && lastHoveredTarget.__zr) {
|
this.dispatchToElement(lastHovered, 'mouseout', event);
|
}
|
|
// Mouse moving on one element
|
this.dispatchToElement(hovered, 'mousemove', event);
|
|
// Mouse over on a new element
|
if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {
|
this.dispatchToElement(hovered, 'mouseover', event);
|
}
|
},
|
|
mouseout: function (event) {
|
this.dispatchToElement(this._hovered, 'mouseout', event);
|
|
// There might be some doms created by upper layer application
|
// at the same level of painter.getViewportRoot() (e.g., tooltip
|
// dom created by echarts), where 'globalout' event should not
|
// be triggered when mouse enters these doms. (But 'mouseout'
|
// should be triggered at the original hovered element as usual).
|
var element = event.toElement || event.relatedTarget;
|
var innerDom;
|
do {
|
element = element && element.parentNode;
|
}
|
while (element && element.nodeType != 9 && !(
|
innerDom = element === this.painterRoot
|
));
|
|
!innerDom && this.trigger('globalout', {event: event});
|
},
|
|
/**
|
* Resize
|
*/
|
resize: function (event) {
|
this._hovered = {};
|
},
|
|
/**
|
* Dispatch event
|
* @param {string} eventName
|
* @param {event=} eventArgs
|
*/
|
dispatch: function (eventName, eventArgs) {
|
var handler = this[eventName];
|
handler && handler.call(this, eventArgs);
|
},
|
|
/**
|
* Dispose
|
*/
|
dispose: function () {
|
|
this.proxy.dispose();
|
|
this.storage =
|
this.proxy =
|
this.painter = null;
|
},
|
|
/**
|
* 设置默认的cursor style
|
* @param {string} [cursorStyle='default'] 例如 crosshair
|
*/
|
setCursorStyle: function (cursorStyle) {
|
var proxy = this.proxy;
|
proxy.setCursor && proxy.setCursor(cursorStyle);
|
},
|
|
/**
|
* 事件分发代理
|
*
|
* @private
|
* @param {Object} targetInfo {target, topTarget} 目标图形元素
|
* @param {string} eventName 事件名称
|
* @param {Object} event 事件对象
|
*/
|
dispatchToElement: function (targetInfo, eventName, event) {
|
targetInfo = targetInfo || {};
|
var eventHandler = 'on' + eventName;
|
var eventPacket = makeEventPacket(eventName, targetInfo, event);
|
|
var el = targetInfo.target;
|
while (el) {
|
el[eventHandler]
|
&& (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket));
|
|
el.trigger(eventName, eventPacket);
|
|
el = el.parent;
|
|
if (eventPacket.cancelBubble) {
|
break;
|
}
|
}
|
|
if (!eventPacket.cancelBubble) {
|
// 冒泡到顶级 zrender 对象
|
this.trigger(eventName, eventPacket);
|
// 分发事件到用户自定义层
|
// 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在
|
this.painter && this.painter.eachOtherLayer(function (layer) {
|
if (typeof(layer[eventHandler]) == 'function') {
|
layer[eventHandler].call(layer, eventPacket);
|
}
|
if (layer.trigger) {
|
layer.trigger(eventName, eventPacket);
|
}
|
});
|
}
|
},
|
|
/**
|
* @private
|
* @param {number} x
|
* @param {number} y
|
* @param {module:zrender/graphic/Displayable} exclude
|
* @return {model:zrender/Element}
|
* @method
|
*/
|
findHover: function(x, y, exclude) {
|
var list = this.storage.getDisplayList();
|
var out = {};
|
|
for (var i = list.length - 1; i >= 0 ; i--) {
|
var hoverCheckResult;
|
if (list[i] !== exclude
|
// getDisplayList may include ignored item in VML mode
|
&& !list[i].ignore
|
&& (hoverCheckResult = isHover(list[i], x, y))
|
) {
|
!out.topTarget && (out.topTarget = list[i]);
|
if (hoverCheckResult !== SILENT) {
|
out.target = list[i];
|
break;
|
}
|
}
|
}
|
|
return out;
|
}
|
};
|
|
// Common handlers
|
util.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
|
Handler.prototype[name] = function (event) {
|
// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover
|
var hovered = this.findHover(event.zrX, event.zrY);
|
var hoveredTarget = hovered.target;
|
|
if (name === 'mousedown') {
|
this._downel = hoveredTarget;
|
// In case click triggered before mouseup
|
this._upel = hoveredTarget;
|
}
|
else if (name === 'mosueup') {
|
this._upel = hoveredTarget;
|
}
|
else if (name === 'click') {
|
if (this._downel !== this._upel) {
|
return;
|
}
|
}
|
|
this.dispatchToElement(hovered, name, event);
|
};
|
});
|
|
function isHover(displayable, x, y) {
|
if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {
|
var el = displayable;
|
var isSilent;
|
while (el) {
|
// If clipped by ancestor.
|
if (el.clipPath && !el.clipPath.contain(x, y)) {
|
return false;
|
}
|
if (el.silent) {
|
isSilent = true;
|
}
|
el = el.parent;
|
}
|
return isSilent ? SILENT : true;
|
}
|
|
return false;
|
}
|
|
util.mixin(Handler, Eventful);
|
util.mixin(Handler, Draggable);
|
|
module.exports = Handler;
|
|
|
/***/ },
|
/* 84 */
|
/***/ function(module, exports) {
|
|
// TODO Draggable for group
|
// FIXME Draggable on element which has parent rotation or scale
|
|
function Draggable() {
|
|
this.on('mousedown', this._dragStart, this);
|
this.on('mousemove', this._drag, this);
|
this.on('mouseup', this._dragEnd, this);
|
this.on('globalout', this._dragEnd, this);
|
// this._dropTarget = null;
|
// this._draggingTarget = null;
|
|
// this._x = 0;
|
// this._y = 0;
|
}
|
|
Draggable.prototype = {
|
|
constructor: Draggable,
|
|
_dragStart: function (e) {
|
var draggingTarget = e.target;
|
if (draggingTarget && draggingTarget.draggable) {
|
this._draggingTarget = draggingTarget;
|
draggingTarget.dragging = true;
|
this._x = e.offsetX;
|
this._y = e.offsetY;
|
|
this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
|
}
|
},
|
|
_drag: function (e) {
|
var draggingTarget = this._draggingTarget;
|
if (draggingTarget) {
|
|
var x = e.offsetX;
|
var y = e.offsetY;
|
|
var dx = x - this._x;
|
var dy = y - this._y;
|
this._x = x;
|
this._y = y;
|
|
draggingTarget.drift(dx, dy, e);
|
this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
|
|
var dropTarget = this.findHover(x, y, draggingTarget).target;
|
var lastDropTarget = this._dropTarget;
|
this._dropTarget = dropTarget;
|
|
if (draggingTarget !== dropTarget) {
|
if (lastDropTarget && dropTarget !== lastDropTarget) {
|
this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
|
}
|
if (dropTarget && dropTarget !== lastDropTarget) {
|
this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
|
}
|
}
|
}
|
},
|
|
_dragEnd: function (e) {
|
var draggingTarget = this._draggingTarget;
|
|
if (draggingTarget) {
|
draggingTarget.dragging = false;
|
}
|
|
this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
|
|
if (this._dropTarget) {
|
this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
|
}
|
|
this._draggingTarget = null;
|
this._dropTarget = null;
|
}
|
|
};
|
|
function param(target, e) {
|
return {target: target, topTarget: e && e.topTarget};
|
}
|
|
module.exports = Draggable;
|
|
|
/***/ },
|
/* 85 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Storage内容仓库模块
|
* @module zrender/Storage
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* @author errorrik (errorrik@gmail.com)
|
* @author pissang (https://github.com/pissang/)
|
*/
|
|
|
var util = __webpack_require__(4);
|
var env = __webpack_require__(2);
|
|
var Group = __webpack_require__(30);
|
|
// Use timsort because in most case elements are partially sorted
|
// https://jsfiddle.net/pissang/jr4x7mdm/8/
|
var timsort = __webpack_require__(86);
|
|
function shapeCompareFunc(a, b) {
|
if (a.zlevel === b.zlevel) {
|
if (a.z === b.z) {
|
// if (a.z2 === b.z2) {
|
// // FIXME Slow has renderidx compare
|
// // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement
|
// // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012
|
// return a.__renderidx - b.__renderidx;
|
// }
|
return a.z2 - b.z2;
|
}
|
return a.z - b.z;
|
}
|
return a.zlevel - b.zlevel;
|
}
|
/**
|
* 内容仓库 (M)
|
* @alias module:zrender/Storage
|
* @constructor
|
*/
|
var Storage = function () {
|
this._roots = [];
|
|
this._displayList = [];
|
|
this._displayListLen = 0;
|
};
|
|
Storage.prototype = {
|
|
constructor: Storage,
|
|
/**
|
* @param {Function} cb
|
*
|
*/
|
traverse: function (cb, context) {
|
for (var i = 0; i < this._roots.length; i++) {
|
this._roots[i].traverse(cb, context);
|
}
|
},
|
|
/**
|
* 返回所有图形的绘制队列
|
* @param {boolean} [update=false] 是否在返回前更新该数组
|
* @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效
|
*
|
* 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList}
|
* @return {Array.<module:zrender/graphic/Displayable>}
|
*/
|
getDisplayList: function (update, includeIgnore) {
|
includeIgnore = includeIgnore || false;
|
if (update) {
|
this.updateDisplayList(includeIgnore);
|
}
|
return this._displayList;
|
},
|
|
/**
|
* 更新图形的绘制队列。
|
* 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,
|
* 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列
|
* @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组
|
*/
|
updateDisplayList: function (includeIgnore) {
|
this._displayListLen = 0;
|
var roots = this._roots;
|
var displayList = this._displayList;
|
for (var i = 0, len = roots.length; i < len; i++) {
|
this._updateAndAddDisplayable(roots[i], null, includeIgnore);
|
}
|
displayList.length = this._displayListLen;
|
|
// for (var i = 0, len = displayList.length; i < len; i++) {
|
// displayList[i].__renderidx = i;
|
// }
|
|
// displayList.sort(shapeCompareFunc);
|
env.canvasSupported && timsort(displayList, shapeCompareFunc);
|
},
|
|
_updateAndAddDisplayable: function (el, clipPaths, includeIgnore) {
|
|
if (el.ignore && !includeIgnore) {
|
return;
|
}
|
|
el.beforeUpdate();
|
|
if (el.__dirty) {
|
|
el.update();
|
|
}
|
|
el.afterUpdate();
|
|
var userSetClipPath = el.clipPath;
|
if (userSetClipPath) {
|
|
// FIXME 效率影响
|
if (clipPaths) {
|
clipPaths = clipPaths.slice();
|
}
|
else {
|
clipPaths = [];
|
}
|
|
var currentClipPath = userSetClipPath;
|
var parentClipPath = el;
|
// Recursively add clip path
|
while (currentClipPath) {
|
// clipPath 的变换是基于使用这个 clipPath 的元素
|
currentClipPath.parent = parentClipPath;
|
currentClipPath.updateTransform();
|
|
clipPaths.push(currentClipPath);
|
|
parentClipPath = currentClipPath;
|
currentClipPath = currentClipPath.clipPath;
|
}
|
}
|
|
if (el.isGroup) {
|
var children = el._children;
|
|
for (var i = 0; i < children.length; i++) {
|
var child = children[i];
|
|
// Force to mark as dirty if group is dirty
|
// FIXME __dirtyPath ?
|
if (el.__dirty) {
|
child.__dirty = true;
|
}
|
|
this._updateAndAddDisplayable(child, clipPaths, includeIgnore);
|
}
|
|
// Mark group clean here
|
el.__dirty = false;
|
|
}
|
else {
|
el.__clipPaths = clipPaths;
|
|
this._displayList[this._displayListLen++] = el;
|
}
|
},
|
|
/**
|
* 添加图形(Shape)或者组(Group)到根节点
|
* @param {module:zrender/Element} el
|
*/
|
addRoot: function (el) {
|
if (el.__storage === this) {
|
return;
|
}
|
|
if (el instanceof Group) {
|
el.addChildrenToStorage(this);
|
}
|
|
this.addToStorage(el);
|
this._roots.push(el);
|
},
|
|
/**
|
* 删除指定的图形(Shape)或者组(Group)
|
* @param {string|Array.<string>} [el] 如果为空清空整个Storage
|
*/
|
delRoot: function (el) {
|
if (el == null) {
|
// 不指定el清空
|
for (var i = 0; i < this._roots.length; i++) {
|
var root = this._roots[i];
|
if (root instanceof Group) {
|
root.delChildrenFromStorage(this);
|
}
|
}
|
|
this._roots = [];
|
this._displayList = [];
|
this._displayListLen = 0;
|
|
return;
|
}
|
|
if (el instanceof Array) {
|
for (var i = 0, l = el.length; i < l; i++) {
|
this.delRoot(el[i]);
|
}
|
return;
|
}
|
|
|
var idx = util.indexOf(this._roots, el);
|
if (idx >= 0) {
|
this.delFromStorage(el);
|
this._roots.splice(idx, 1);
|
if (el instanceof Group) {
|
el.delChildrenFromStorage(this);
|
}
|
}
|
},
|
|
addToStorage: function (el) {
|
el.__storage = this;
|
el.dirty(false);
|
|
return this;
|
},
|
|
delFromStorage: function (el) {
|
if (el) {
|
el.__storage = null;
|
}
|
|
return this;
|
},
|
|
/**
|
* 清空并且释放Storage
|
*/
|
dispose: function () {
|
this._renderList =
|
this._roots = null;
|
},
|
|
displayableSortFunc: shapeCompareFunc
|
};
|
|
module.exports = Storage;
|
|
|
|
/***/ },
|
/* 86 */
|
/***/ function(module, exports) {
|
|
// https://github.com/mziccard/node-timsort
|
|
var DEFAULT_MIN_MERGE = 32;
|
|
var DEFAULT_MIN_GALLOPING = 7;
|
|
var DEFAULT_TMP_STORAGE_LENGTH = 256;
|
|
function minRunLength(n) {
|
var r = 0;
|
|
while (n >= DEFAULT_MIN_MERGE) {
|
r |= n & 1;
|
n >>= 1;
|
}
|
|
return n + r;
|
}
|
|
function makeAscendingRun(array, lo, hi, compare) {
|
var runHi = lo + 1;
|
|
if (runHi === hi) {
|
return 1;
|
}
|
|
if (compare(array[runHi++], array[lo]) < 0) {
|
while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
|
runHi++;
|
}
|
|
reverseRun(array, lo, runHi);
|
}
|
else {
|
while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
|
runHi++;
|
}
|
}
|
|
return runHi - lo;
|
}
|
|
function reverseRun(array, lo, hi) {
|
hi--;
|
|
while (lo < hi) {
|
var t = array[lo];
|
array[lo++] = array[hi];
|
array[hi--] = t;
|
}
|
}
|
|
function binaryInsertionSort(array, lo, hi, start, compare) {
|
if (start === lo) {
|
start++;
|
}
|
|
for (; start < hi; start++) {
|
var pivot = array[start];
|
|
var left = lo;
|
var right = start;
|
var mid;
|
|
while (left < right) {
|
mid = left + right >>> 1;
|
|
if (compare(pivot, array[mid]) < 0) {
|
right = mid;
|
}
|
else {
|
left = mid + 1;
|
}
|
}
|
|
var n = start - left;
|
|
switch (n) {
|
case 3:
|
array[left + 3] = array[left + 2];
|
|
case 2:
|
array[left + 2] = array[left + 1];
|
|
case 1:
|
array[left + 1] = array[left];
|
break;
|
default:
|
while (n > 0) {
|
array[left + n] = array[left + n - 1];
|
n--;
|
}
|
}
|
|
array[left] = pivot;
|
}
|
}
|
|
function gallopLeft(value, array, start, length, hint, compare) {
|
var lastOffset = 0;
|
var maxOffset = 0;
|
var offset = 1;
|
|
if (compare(value, array[start + hint]) > 0) {
|
maxOffset = length - hint;
|
|
while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {
|
lastOffset = offset;
|
offset = (offset << 1) + 1;
|
|
if (offset <= 0) {
|
offset = maxOffset;
|
}
|
}
|
|
if (offset > maxOffset) {
|
offset = maxOffset;
|
}
|
|
lastOffset += hint;
|
offset += hint;
|
}
|
else {
|
maxOffset = hint + 1;
|
while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {
|
lastOffset = offset;
|
offset = (offset << 1) + 1;
|
|
if (offset <= 0) {
|
offset = maxOffset;
|
}
|
}
|
if (offset > maxOffset) {
|
offset = maxOffset;
|
}
|
|
var tmp = lastOffset;
|
lastOffset = hint - offset;
|
offset = hint - tmp;
|
}
|
|
lastOffset++;
|
while (lastOffset < offset) {
|
var m = lastOffset + (offset - lastOffset >>> 1);
|
|
if (compare(value, array[start + m]) > 0) {
|
lastOffset = m + 1;
|
}
|
else {
|
offset = m;
|
}
|
}
|
return offset;
|
}
|
|
function gallopRight(value, array, start, length, hint, compare) {
|
var lastOffset = 0;
|
var maxOffset = 0;
|
var offset = 1;
|
|
if (compare(value, array[start + hint]) < 0) {
|
maxOffset = hint + 1;
|
|
while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {
|
lastOffset = offset;
|
offset = (offset << 1) + 1;
|
|
if (offset <= 0) {
|
offset = maxOffset;
|
}
|
}
|
|
if (offset > maxOffset) {
|
offset = maxOffset;
|
}
|
|
var tmp = lastOffset;
|
lastOffset = hint - offset;
|
offset = hint - tmp;
|
}
|
else {
|
maxOffset = length - hint;
|
|
while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {
|
lastOffset = offset;
|
offset = (offset << 1) + 1;
|
|
if (offset <= 0) {
|
offset = maxOffset;
|
}
|
}
|
|
if (offset > maxOffset) {
|
offset = maxOffset;
|
}
|
|
lastOffset += hint;
|
offset += hint;
|
}
|
|
lastOffset++;
|
|
while (lastOffset < offset) {
|
var m = lastOffset + (offset - lastOffset >>> 1);
|
|
if (compare(value, array[start + m]) < 0) {
|
offset = m;
|
}
|
else {
|
lastOffset = m + 1;
|
}
|
}
|
|
return offset;
|
}
|
|
function TimSort(array, compare) {
|
var minGallop = DEFAULT_MIN_GALLOPING;
|
var length = 0;
|
var tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH;
|
var stackLength = 0;
|
var runStart;
|
var runLength;
|
var stackSize = 0;
|
|
length = array.length;
|
|
if (length < 2 * DEFAULT_TMP_STORAGE_LENGTH) {
|
tmpStorageLength = length >>> 1;
|
}
|
|
var tmp = [];
|
|
stackLength = length < 120 ? 5 : length < 1542 ? 10 : length < 119151 ? 19 : 40;
|
|
runStart = [];
|
runLength = [];
|
|
function pushRun(_runStart, _runLength) {
|
runStart[stackSize] = _runStart;
|
runLength[stackSize] = _runLength;
|
stackSize += 1;
|
}
|
|
function mergeRuns() {
|
while (stackSize > 1) {
|
var n = stackSize - 2;
|
|
if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) {
|
if (runLength[n - 1] < runLength[n + 1]) {
|
n--;
|
}
|
}
|
else if (runLength[n] > runLength[n + 1]) {
|
break;
|
}
|
mergeAt(n);
|
}
|
}
|
|
function forceMergeRuns() {
|
while (stackSize > 1) {
|
var n = stackSize - 2;
|
|
if (n > 0 && runLength[n - 1] < runLength[n + 1]) {
|
n--;
|
}
|
|
mergeAt(n);
|
}
|
}
|
|
function mergeAt(i) {
|
var start1 = runStart[i];
|
var length1 = runLength[i];
|
var start2 = runStart[i + 1];
|
var length2 = runLength[i + 1];
|
|
runLength[i] = length1 + length2;
|
|
if (i === stackSize - 3) {
|
runStart[i + 1] = runStart[i + 2];
|
runLength[i + 1] = runLength[i + 2];
|
}
|
|
stackSize--;
|
|
var k = gallopRight(array[start2], array, start1, length1, 0, compare);
|
start1 += k;
|
length1 -= k;
|
|
if (length1 === 0) {
|
return;
|
}
|
|
length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);
|
|
if (length2 === 0) {
|
return;
|
}
|
|
if (length1 <= length2) {
|
mergeLow(start1, length1, start2, length2);
|
}
|
else {
|
mergeHigh(start1, length1, start2, length2);
|
}
|
}
|
|
function mergeLow(start1, length1, start2, length2) {
|
var i = 0;
|
|
for (i = 0; i < length1; i++) {
|
tmp[i] = array[start1 + i];
|
}
|
|
var cursor1 = 0;
|
var cursor2 = start2;
|
var dest = start1;
|
|
array[dest++] = array[cursor2++];
|
|
if (--length2 === 0) {
|
for (i = 0; i < length1; i++) {
|
array[dest + i] = tmp[cursor1 + i];
|
}
|
return;
|
}
|
|
if (length1 === 1) {
|
for (i = 0; i < length2; i++) {
|
array[dest + i] = array[cursor2 + i];
|
}
|
array[dest + length2] = tmp[cursor1];
|
return;
|
}
|
|
var _minGallop = minGallop;
|
var count1, count2, exit;
|
|
while (1) {
|
count1 = 0;
|
count2 = 0;
|
exit = false;
|
|
do {
|
if (compare(array[cursor2], tmp[cursor1]) < 0) {
|
array[dest++] = array[cursor2++];
|
count2++;
|
count1 = 0;
|
|
if (--length2 === 0) {
|
exit = true;
|
break;
|
}
|
}
|
else {
|
array[dest++] = tmp[cursor1++];
|
count1++;
|
count2 = 0;
|
if (--length1 === 1) {
|
exit = true;
|
break;
|
}
|
}
|
} while ((count1 | count2) < _minGallop);
|
|
if (exit) {
|
break;
|
}
|
|
do {
|
count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);
|
|
if (count1 !== 0) {
|
for (i = 0; i < count1; i++) {
|
array[dest + i] = tmp[cursor1 + i];
|
}
|
|
dest += count1;
|
cursor1 += count1;
|
length1 -= count1;
|
if (length1 <= 1) {
|
exit = true;
|
break;
|
}
|
}
|
|
array[dest++] = array[cursor2++];
|
|
if (--length2 === 0) {
|
exit = true;
|
break;
|
}
|
|
count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);
|
|
if (count2 !== 0) {
|
for (i = 0; i < count2; i++) {
|
array[dest + i] = array[cursor2 + i];
|
}
|
|
dest += count2;
|
cursor2 += count2;
|
length2 -= count2;
|
|
if (length2 === 0) {
|
exit = true;
|
break;
|
}
|
}
|
array[dest++] = tmp[cursor1++];
|
|
if (--length1 === 1) {
|
exit = true;
|
break;
|
}
|
|
_minGallop--;
|
} while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
|
|
if (exit) {
|
break;
|
}
|
|
if (_minGallop < 0) {
|
_minGallop = 0;
|
}
|
|
_minGallop += 2;
|
}
|
|
minGallop = _minGallop;
|
|
minGallop < 1 && (minGallop = 1);
|
|
if (length1 === 1) {
|
for (i = 0; i < length2; i++) {
|
array[dest + i] = array[cursor2 + i];
|
}
|
array[dest + length2] = tmp[cursor1];
|
}
|
else if (length1 === 0) {
|
throw new Error();
|
// throw new Error('mergeLow preconditions were not respected');
|
}
|
else {
|
for (i = 0; i < length1; i++) {
|
array[dest + i] = tmp[cursor1 + i];
|
}
|
}
|
}
|
|
function mergeHigh (start1, length1, start2, length2) {
|
var i = 0;
|
|
for (i = 0; i < length2; i++) {
|
tmp[i] = array[start2 + i];
|
}
|
|
var cursor1 = start1 + length1 - 1;
|
var cursor2 = length2 - 1;
|
var dest = start2 + length2 - 1;
|
var customCursor = 0;
|
var customDest = 0;
|
|
array[dest--] = array[cursor1--];
|
|
if (--length1 === 0) {
|
customCursor = dest - (length2 - 1);
|
|
for (i = 0; i < length2; i++) {
|
array[customCursor + i] = tmp[i];
|
}
|
|
return;
|
}
|
|
if (length2 === 1) {
|
dest -= length1;
|
cursor1 -= length1;
|
customDest = dest + 1;
|
customCursor = cursor1 + 1;
|
|
for (i = length1 - 1; i >= 0; i--) {
|
array[customDest + i] = array[customCursor + i];
|
}
|
|
array[dest] = tmp[cursor2];
|
return;
|
}
|
|
var _minGallop = minGallop;
|
|
while (true) {
|
var count1 = 0;
|
var count2 = 0;
|
var exit = false;
|
|
do {
|
if (compare(tmp[cursor2], array[cursor1]) < 0) {
|
array[dest--] = array[cursor1--];
|
count1++;
|
count2 = 0;
|
if (--length1 === 0) {
|
exit = true;
|
break;
|
}
|
}
|
else {
|
array[dest--] = tmp[cursor2--];
|
count2++;
|
count1 = 0;
|
if (--length2 === 1) {
|
exit = true;
|
break;
|
}
|
}
|
} while ((count1 | count2) < _minGallop);
|
|
if (exit) {
|
break;
|
}
|
|
do {
|
count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);
|
|
if (count1 !== 0) {
|
dest -= count1;
|
cursor1 -= count1;
|
length1 -= count1;
|
customDest = dest + 1;
|
customCursor = cursor1 + 1;
|
|
for (i = count1 - 1; i >= 0; i--) {
|
array[customDest + i] = array[customCursor + i];
|
}
|
|
if (length1 === 0) {
|
exit = true;
|
break;
|
}
|
}
|
|
array[dest--] = tmp[cursor2--];
|
|
if (--length2 === 1) {
|
exit = true;
|
break;
|
}
|
|
count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);
|
|
if (count2 !== 0) {
|
dest -= count2;
|
cursor2 -= count2;
|
length2 -= count2;
|
customDest = dest + 1;
|
customCursor = cursor2 + 1;
|
|
for (i = 0; i < count2; i++) {
|
array[customDest + i] = tmp[customCursor + i];
|
}
|
|
if (length2 <= 1) {
|
exit = true;
|
break;
|
}
|
}
|
|
array[dest--] = array[cursor1--];
|
|
if (--length1 === 0) {
|
exit = true;
|
break;
|
}
|
|
_minGallop--;
|
} while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);
|
|
if (exit) {
|
break;
|
}
|
|
if (_minGallop < 0) {
|
_minGallop = 0;
|
}
|
|
_minGallop += 2;
|
}
|
|
minGallop = _minGallop;
|
|
if (minGallop < 1) {
|
minGallop = 1;
|
}
|
|
if (length2 === 1) {
|
dest -= length1;
|
cursor1 -= length1;
|
customDest = dest + 1;
|
customCursor = cursor1 + 1;
|
|
for (i = length1 - 1; i >= 0; i--) {
|
array[customDest + i] = array[customCursor + i];
|
}
|
|
array[dest] = tmp[cursor2];
|
}
|
else if (length2 === 0) {
|
throw new Error();
|
// throw new Error('mergeHigh preconditions were not respected');
|
}
|
else {
|
customCursor = dest - (length2 - 1);
|
for (i = 0; i < length2; i++) {
|
array[customCursor + i] = tmp[i];
|
}
|
}
|
}
|
|
this.mergeRuns = mergeRuns;
|
this.forceMergeRuns = forceMergeRuns;
|
this.pushRun = pushRun;
|
}
|
|
function sort(array, compare, lo, hi) {
|
if (!lo) {
|
lo = 0;
|
}
|
if (!hi) {
|
hi = array.length;
|
}
|
|
var remaining = hi - lo;
|
|
if (remaining < 2) {
|
return;
|
}
|
|
var runLength = 0;
|
|
if (remaining < DEFAULT_MIN_MERGE) {
|
runLength = makeAscendingRun(array, lo, hi, compare);
|
binaryInsertionSort(array, lo, hi, lo + runLength, compare);
|
return;
|
}
|
|
var ts = new TimSort(array, compare);
|
|
var minRun = minRunLength(remaining);
|
|
do {
|
runLength = makeAscendingRun(array, lo, hi, compare);
|
if (runLength < minRun) {
|
var force = remaining;
|
if (force > minRun) {
|
force = minRun;
|
}
|
|
binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);
|
runLength = force;
|
}
|
|
ts.pushRun(lo, runLength);
|
ts.mergeRuns();
|
|
remaining -= runLength;
|
lo += runLength;
|
} while (remaining !== 0);
|
|
ts.forceMergeRuns();
|
}
|
|
module.exports = sort;
|
|
|
/***/ },
|
/* 87 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 动画主类, 调度和管理所有动画控制器
|
*
|
* @module zrender/animation/Animation
|
* @author pissang(https://github.com/pissang)
|
*/
|
// TODO Additive animation
|
// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
|
// https://developer.apple.com/videos/wwdc2014/#236
|
|
|
var util = __webpack_require__(4);
|
var Dispatcher = __webpack_require__(88).Dispatcher;
|
|
var requestAnimationFrame = __webpack_require__(89);
|
|
var Animator = __webpack_require__(36);
|
/**
|
* @typedef {Object} IZRenderStage
|
* @property {Function} update
|
*/
|
|
/**
|
* @alias module:zrender/animation/Animation
|
* @constructor
|
* @param {Object} [options]
|
* @param {Function} [options.onframe]
|
* @param {IZRenderStage} [options.stage]
|
* @example
|
* var animation = new Animation();
|
* var obj = {
|
* x: 100,
|
* y: 100
|
* };
|
* animation.animate(node.position)
|
* .when(1000, {
|
* x: 500,
|
* y: 500
|
* })
|
* .when(2000, {
|
* x: 100,
|
* y: 100
|
* })
|
* .start('spline');
|
*/
|
var Animation = function (options) {
|
|
options = options || {};
|
|
this.stage = options.stage || {};
|
|
this.onframe = options.onframe || function() {};
|
|
// private properties
|
this._clips = [];
|
|
this._running = false;
|
|
this._time;
|
|
this._pausedTime;
|
|
this._pauseStart;
|
|
this._paused = false;
|
|
Dispatcher.call(this);
|
};
|
|
Animation.prototype = {
|
|
constructor: Animation,
|
/**
|
* 添加 clip
|
* @param {module:zrender/animation/Clip} clip
|
*/
|
addClip: function (clip) {
|
this._clips.push(clip);
|
},
|
/**
|
* 添加 animator
|
* @param {module:zrender/animation/Animator} animator
|
*/
|
addAnimator: function (animator) {
|
animator.animation = this;
|
var clips = animator.getClips();
|
for (var i = 0; i < clips.length; i++) {
|
this.addClip(clips[i]);
|
}
|
},
|
/**
|
* 删除动画片段
|
* @param {module:zrender/animation/Clip} clip
|
*/
|
removeClip: function(clip) {
|
var idx = util.indexOf(this._clips, clip);
|
if (idx >= 0) {
|
this._clips.splice(idx, 1);
|
}
|
},
|
|
/**
|
* 删除动画片段
|
* @param {module:zrender/animation/Animator} animator
|
*/
|
removeAnimator: function (animator) {
|
var clips = animator.getClips();
|
for (var i = 0; i < clips.length; i++) {
|
this.removeClip(clips[i]);
|
}
|
animator.animation = null;
|
},
|
|
_update: function() {
|
|
var time = new Date().getTime() - this._pausedTime;
|
var delta = time - this._time;
|
var clips = this._clips;
|
var len = clips.length;
|
|
var deferredEvents = [];
|
var deferredClips = [];
|
for (var i = 0; i < len; i++) {
|
var clip = clips[i];
|
var e = clip.step(time, delta);
|
// Throw out the events need to be called after
|
// stage.update, like destroy
|
if (e) {
|
deferredEvents.push(e);
|
deferredClips.push(clip);
|
}
|
}
|
|
// Remove the finished clip
|
for (var i = 0; i < len;) {
|
if (clips[i]._needsRemove) {
|
clips[i] = clips[len - 1];
|
clips.pop();
|
len--;
|
}
|
else {
|
i++;
|
}
|
}
|
|
len = deferredEvents.length;
|
for (var i = 0; i < len; i++) {
|
deferredClips[i].fire(deferredEvents[i]);
|
}
|
|
this._time = time;
|
|
this.onframe(delta);
|
|
this.trigger('frame', delta);
|
|
if (this.stage.update) {
|
this.stage.update();
|
}
|
},
|
|
_startLoop: function () {
|
var self = this;
|
|
this._running = true;
|
|
function step() {
|
if (self._running) {
|
|
requestAnimationFrame(step);
|
|
!self._paused && self._update();
|
}
|
}
|
|
requestAnimationFrame(step);
|
},
|
|
/**
|
* 开始运行动画
|
*/
|
start: function () {
|
|
this._time = new Date().getTime();
|
this._pausedTime = 0;
|
|
this._startLoop();
|
},
|
/**
|
* 停止运行动画
|
*/
|
stop: function () {
|
this._running = false;
|
},
|
|
/**
|
* Pause
|
*/
|
pause: function () {
|
if (!this._paused) {
|
this._pauseStart = new Date().getTime();
|
this._paused = true;
|
}
|
},
|
|
/**
|
* Resume
|
*/
|
resume: function () {
|
if (this._paused) {
|
this._pausedTime += (new Date().getTime()) - this._pauseStart;
|
this._paused = false;
|
}
|
},
|
|
/**
|
* 清除所有动画片段
|
*/
|
clear: function () {
|
this._clips = [];
|
},
|
/**
|
* 对一个目标创建一个animator对象,可以指定目标中的属性使用动画
|
* @param {Object} target
|
* @param {Object} options
|
* @param {boolean} [options.loop=false] 是否循环播放动画
|
* @param {Function} [options.getter=null]
|
* 如果指定getter函数,会通过getter函数取属性值
|
* @param {Function} [options.setter=null]
|
* 如果指定setter函数,会通过setter函数设置属性值
|
* @return {module:zrender/animation/Animation~Animator}
|
*/
|
// TODO Gap
|
animate: function (target, options) {
|
options = options || {};
|
|
var animator = new Animator(
|
target,
|
options.loop,
|
options.getter,
|
options.setter
|
);
|
|
this.addAnimator(animator);
|
|
return animator;
|
}
|
};
|
|
util.mixin(Animation, Dispatcher);
|
|
module.exports = Animation;
|
|
|
|
/***/ },
|
/* 88 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* 事件辅助类
|
* @module zrender/core/event
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
*/
|
|
|
var Eventful = __webpack_require__(33);
|
var env = __webpack_require__(2);
|
|
var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
|
|
function getBoundingClientRect(el) {
|
// BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect
|
return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0};
|
}
|
|
// `calculate` is optional, default false
|
function clientToLocal(el, e, out, calculate) {
|
out = out || {};
|
|
// According to the W3C Working Draft, offsetX and offsetY should be relative
|
// to the padding edge of the target element. The only browser using this convention
|
// is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
|
// not support the properties.
|
// (see http://www.jacklmoore.com/notes/mouse-position/)
|
// In zr painter.dom, padding edge equals to border edge.
|
|
// FIXME
|
// When mousemove event triggered on ec tooltip, target is not zr painter.dom, and
|
// offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y
|
// is too complex. So css-transfrom dont support in this case temporarily.
|
if (calculate || !env.canvasSupported) {
|
defaultGetZrXY(el, e, out);
|
}
|
// Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
|
// ancestor element, so we should make sure el is positioned (e.g., not position:static).
|
// BTW1, Webkit don't return the same results as FF in non-simple cases (like add
|
// zoom-factor, overflow / opacity layers, transforms ...)
|
// BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
|
// <https://bugs.jquery.com/ticket/8523#comment:14>
|
// BTW3, In ff, offsetX/offsetY is always 0.
|
else if (env.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) {
|
out.zrX = e.layerX;
|
out.zrY = e.layerY;
|
}
|
// For IE6+, chrome, safari, opera. (When will ff support offsetX?)
|
else if (e.offsetX != null) {
|
out.zrX = e.offsetX;
|
out.zrY = e.offsetY;
|
}
|
// For some other device, e.g., IOS safari.
|
else {
|
defaultGetZrXY(el, e, out);
|
}
|
|
return out;
|
}
|
|
function defaultGetZrXY(el, e, out) {
|
// This well-known method below does not support css transform.
|
var box = getBoundingClientRect(el);
|
out.zrX = e.clientX - box.left;
|
out.zrY = e.clientY - box.top;
|
}
|
|
/**
|
* 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标.
|
* `calculate` is optional, default false.
|
*/
|
function normalizeEvent(el, e, calculate) {
|
|
e = e || window.event;
|
|
if (e.zrX != null) {
|
return e;
|
}
|
|
var eventType = e.type;
|
var isTouch = eventType && eventType.indexOf('touch') >= 0;
|
|
if (!isTouch) {
|
clientToLocal(el, e, e, calculate);
|
e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;
|
}
|
else {
|
var touch = eventType != 'touchend'
|
? e.targetTouches[0]
|
: e.changedTouches[0];
|
touch && clientToLocal(el, touch, e, calculate);
|
}
|
|
return e;
|
}
|
|
function addEventListener(el, name, handler) {
|
if (isDomLevel2) {
|
el.addEventListener(name, handler);
|
}
|
else {
|
el.attachEvent('on' + name, handler);
|
}
|
}
|
|
function removeEventListener(el, name, handler) {
|
if (isDomLevel2) {
|
el.removeEventListener(name, handler);
|
}
|
else {
|
el.detachEvent('on' + name, handler);
|
}
|
}
|
|
/**
|
* preventDefault and stopPropagation.
|
* Notice: do not do that in zrender. Upper application
|
* do that if necessary.
|
*
|
* @memberOf module:zrender/core/event
|
* @method
|
* @param {Event} e : event对象
|
*/
|
var stop = isDomLevel2
|
? function (e) {
|
e.preventDefault();
|
e.stopPropagation();
|
e.cancelBubble = true;
|
}
|
: function (e) {
|
e.returnValue = false;
|
e.cancelBubble = true;
|
};
|
|
module.exports = {
|
clientToLocal: clientToLocal,
|
normalizeEvent: normalizeEvent,
|
addEventListener: addEventListener,
|
removeEventListener: removeEventListener,
|
|
stop: stop,
|
// 做向上兼容
|
Dispatcher: Eventful
|
};
|
|
|
|
/***/ },
|
/* 89 */
|
/***/ function(module, exports) {
|
|
|
|
module.exports = (typeof window !== 'undefined' &&
|
(window.requestAnimationFrame
|
|| window.msRequestAnimationFrame
|
|| window.mozRequestAnimationFrame
|
|| window.webkitRequestAnimationFrame))
|
|| function (func) {
|
setTimeout(func, 16);
|
};
|
|
|
|
/***/ },
|
/* 90 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var eventTool = __webpack_require__(88);
|
var zrUtil = __webpack_require__(4);
|
var Eventful = __webpack_require__(33);
|
var env = __webpack_require__(2);
|
var GestureMgr = __webpack_require__(91);
|
|
var addEventListener = eventTool.addEventListener;
|
var removeEventListener = eventTool.removeEventListener;
|
var normalizeEvent = eventTool.normalizeEvent;
|
|
var TOUCH_CLICK_DELAY = 300;
|
|
var mouseHandlerNames = [
|
'click', 'dblclick', 'mousewheel', 'mouseout',
|
'mouseup', 'mousedown', 'mousemove', 'contextmenu'
|
];
|
|
var touchHandlerNames = [
|
'touchstart', 'touchend', 'touchmove'
|
];
|
|
var pointerEventNames = {
|
pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1
|
};
|
|
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) {
|
var nm = name.replace('mouse', 'pointer');
|
return pointerEventNames[nm] ? nm : name;
|
});
|
|
function eventNameFix(name) {
|
return (name === 'mousewheel' && env.browser.firefox) ? 'DOMMouseScroll' : name;
|
}
|
|
function processGesture(proxy, event, stage) {
|
var gestureMgr = proxy._gestureMgr;
|
|
stage === 'start' && gestureMgr.clear();
|
|
var gestureInfo = gestureMgr.recognize(
|
event,
|
proxy.handler.findHover(event.zrX, event.zrY, null).target,
|
proxy.dom
|
);
|
|
stage === 'end' && gestureMgr.clear();
|
|
// Do not do any preventDefault here. Upper application do that if necessary.
|
if (gestureInfo) {
|
var type = gestureInfo.type;
|
event.gestureEvent = type;
|
|
proxy.handler.dispatchToElement({target: gestureInfo.target}, type, gestureInfo.event);
|
}
|
}
|
|
// function onMSGestureChange(proxy, event) {
|
// if (event.translationX || event.translationY) {
|
// // mousemove is carried by MSGesture to reduce the sensitivity.
|
// proxy.handler.dispatchToElement(event.target, 'mousemove', event);
|
// }
|
// if (event.scale !== 1) {
|
// event.pinchX = event.offsetX;
|
// event.pinchY = event.offsetY;
|
// event.pinchScale = event.scale;
|
// proxy.handler.dispatchToElement(event.target, 'pinch', event);
|
// }
|
// }
|
|
/**
|
* Prevent mouse event from being dispatched after Touch Events action
|
* @see <https://github.com/deltakosh/handjs/blob/master/src/hand.base.js>
|
* 1. Mobile browsers dispatch mouse events 300ms after touchend.
|
* 2. Chrome for Android dispatch mousedown for long-touch about 650ms
|
* Result: Blocking Mouse Events for 700ms.
|
*/
|
function setTouchTimer(instance) {
|
instance._touching = true;
|
clearTimeout(instance._touchTimer);
|
instance._touchTimer = setTimeout(function () {
|
instance._touching = false;
|
}, 700);
|
}
|
|
|
var domHandlers = {
|
/**
|
* Mouse move handler
|
* @inner
|
* @param {Event} event
|
*/
|
mousemove: function (event) {
|
event = normalizeEvent(this.dom, event);
|
|
this.trigger('mousemove', event);
|
},
|
|
/**
|
* Mouse out handler
|
* @inner
|
* @param {Event} event
|
*/
|
mouseout: function (event) {
|
event = normalizeEvent(this.dom, event);
|
|
var element = event.toElement || event.relatedTarget;
|
if (element != this.dom) {
|
while (element && element.nodeType != 9) {
|
// 忽略包含在root中的dom引起的mouseOut
|
if (element === this.dom) {
|
return;
|
}
|
|
element = element.parentNode;
|
}
|
}
|
|
this.trigger('mouseout', event);
|
},
|
|
/**
|
* Touch开始响应函数
|
* @inner
|
* @param {Event} event
|
*/
|
touchstart: function (event) {
|
// Default mouse behaviour should not be disabled here.
|
// For example, page may needs to be slided.
|
event = normalizeEvent(this.dom, event);
|
|
// Mark touch, which is useful in distinguish touch and
|
// mouse event in upper applicatoin.
|
event.zrByTouch = true;
|
|
this._lastTouchMoment = new Date();
|
|
processGesture(this, event, 'start');
|
|
// In touch device, trigger `mousemove`(`mouseover`) should
|
// be triggered, and must before `mousedown` triggered.
|
domHandlers.mousemove.call(this, event);
|
|
domHandlers.mousedown.call(this, event);
|
|
setTouchTimer(this);
|
},
|
|
/**
|
* Touch移动响应函数
|
* @inner
|
* @param {Event} event
|
*/
|
touchmove: function (event) {
|
|
event = normalizeEvent(this.dom, event);
|
|
// Mark touch, which is useful in distinguish touch and
|
// mouse event in upper applicatoin.
|
event.zrByTouch = true;
|
|
processGesture(this, event, 'change');
|
|
// Mouse move should always be triggered no matter whether
|
// there is gestrue event, because mouse move and pinch may
|
// be used at the same time.
|
domHandlers.mousemove.call(this, event);
|
|
setTouchTimer(this);
|
},
|
|
/**
|
* Touch结束响应函数
|
* @inner
|
* @param {Event} event
|
*/
|
touchend: function (event) {
|
|
event = normalizeEvent(this.dom, event);
|
|
// Mark touch, which is useful in distinguish touch and
|
// mouse event in upper applicatoin.
|
event.zrByTouch = true;
|
|
processGesture(this, event, 'end');
|
|
domHandlers.mouseup.call(this, event);
|
|
// Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is
|
// triggered in `touchstart`. This seems to be illogical, but by this mechanism,
|
// we can conveniently implement "hover style" in both PC and touch device just
|
// by listening to `mouseover` to add "hover style" and listening to `mouseout`
|
// to remove "hover style" on an element, without any additional code for
|
// compatibility. (`mouseout` will not be triggered in `touchend`, so "hover
|
// style" will remain for user view)
|
|
// click event should always be triggered no matter whether
|
// there is gestrue event. System click can not be prevented.
|
if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) {
|
domHandlers.click.call(this, event);
|
}
|
|
setTouchTimer(this);
|
},
|
|
pointerdown: function (event) {
|
domHandlers.mousedown.call(this, event);
|
|
// if (useMSGuesture(this, event)) {
|
// this._msGesture.addPointer(event.pointerId);
|
// }
|
},
|
|
pointermove: function (event) {
|
// FIXME
|
// pointermove is so sensitive that it always triggered when
|
// tap(click) on touch screen, which affect some judgement in
|
// upper application. So, we dont support mousemove on MS touch
|
// device yet.
|
if (!isPointerFromTouch(event)) {
|
domHandlers.mousemove.call(this, event);
|
}
|
},
|
|
pointerup: function (event) {
|
domHandlers.mouseup.call(this, event);
|
},
|
|
pointerout: function (event) {
|
// pointerout will be triggered when tap on touch screen
|
// (IE11+/Edge on MS Surface) after click event triggered,
|
// which is inconsistent with the mousout behavior we defined
|
// in touchend. So we unify them.
|
// (check domHandlers.touchend for detailed explanation)
|
if (!isPointerFromTouch(event)) {
|
domHandlers.mouseout.call(this, event);
|
}
|
}
|
};
|
|
function isPointerFromTouch(event) {
|
var pointerType = event.pointerType;
|
return pointerType === 'pen' || pointerType === 'touch';
|
}
|
|
// function useMSGuesture(handlerProxy, event) {
|
// return isPointerFromTouch(event) && !!handlerProxy._msGesture;
|
// }
|
|
// Common handlers
|
zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) {
|
domHandlers[name] = function (event) {
|
event = normalizeEvent(this.dom, event);
|
this.trigger(name, event);
|
};
|
});
|
|
/**
|
* 为控制类实例初始化dom 事件处理函数
|
*
|
* @inner
|
* @param {module:zrender/Handler} instance 控制类实例
|
*/
|
function initDomHandler(instance) {
|
zrUtil.each(touchHandlerNames, function (name) {
|
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);
|
});
|
|
zrUtil.each(pointerHandlerNames, function (name) {
|
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance);
|
});
|
|
zrUtil.each(mouseHandlerNames, function (name) {
|
instance._handlers[name] = makeMouseHandler(domHandlers[name], instance);
|
});
|
|
function makeMouseHandler(fn, instance) {
|
return function () {
|
if (instance._touching) {
|
return;
|
}
|
return fn.apply(instance, arguments);
|
};
|
}
|
}
|
|
|
function HandlerDomProxy(dom) {
|
Eventful.call(this);
|
|
this.dom = dom;
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
this._touching = false;
|
|
/**
|
* @private
|
* @type {number}
|
*/
|
this._touchTimer;
|
|
/**
|
* @private
|
* @type {module:zrender/core/GestureMgr}
|
*/
|
this._gestureMgr = new GestureMgr();
|
|
this._handlers = {};
|
|
initDomHandler(this);
|
|
if (env.pointerEventsSupported) { // Only IE11+/Edge
|
// 1. On devices that both enable touch and mouse (e.g., MS Surface and lenovo X240),
|
// IE11+/Edge do not trigger touch event, but trigger pointer event and mouse event
|
// at the same time.
|
// 2. On MS Surface, it probablely only trigger mousedown but no mouseup when tap on
|
// screen, which do not occurs in pointer event.
|
// So we use pointer event to both detect touch gesture and mouse behavior.
|
mountHandlers(pointerHandlerNames, this);
|
|
// FIXME
|
// Note: MS Gesture require CSS touch-action set. But touch-action is not reliable,
|
// which does not prevent defuault behavior occasionally (which may cause view port
|
// zoomed in but use can not zoom it back). And event.preventDefault() does not work.
|
// So we have to not to use MSGesture and not to support touchmove and pinch on MS
|
// touch screen. And we only support click behavior on MS touch screen now.
|
|
// MS Gesture Event is only supported on IE11+/Edge and on Windows 8+.
|
// We dont support touch on IE on win7.
|
// See <https://msdn.microsoft.com/en-us/library/dn433243(v=vs.85).aspx>
|
// if (typeof MSGesture === 'function') {
|
// (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line
|
// dom.addEventListener('MSGestureChange', onMSGestureChange);
|
// }
|
}
|
else {
|
if (env.touchEventsSupported) {
|
mountHandlers(touchHandlerNames, this);
|
// Handler of 'mouseout' event is needed in touch mode, which will be mounted below.
|
// addEventListener(root, 'mouseout', this._mouseoutHandler);
|
}
|
|
// 1. Considering some devices that both enable touch and mouse event (like on MS Surface
|
// and lenovo X240, @see #2350), we make mouse event be always listened, otherwise
|
// mouse event can not be handle in those devices.
|
// 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent
|
// mouseevent after touch event triggered, see `setTouchTimer`.
|
mountHandlers(mouseHandlerNames, this);
|
}
|
|
function mountHandlers(handlerNames, instance) {
|
zrUtil.each(handlerNames, function (name) {
|
addEventListener(dom, eventNameFix(name), instance._handlers[name]);
|
}, instance);
|
}
|
}
|
|
var handlerDomProxyProto = HandlerDomProxy.prototype;
|
handlerDomProxyProto.dispose = function () {
|
var handlerNames = mouseHandlerNames.concat(touchHandlerNames);
|
|
for (var i = 0; i < handlerNames.length; i++) {
|
var name = handlerNames[i];
|
removeEventListener(this.dom, eventNameFix(name), this._handlers[name]);
|
}
|
};
|
|
handlerDomProxyProto.setCursor = function (cursorStyle) {
|
this.dom.style.cursor = cursorStyle || 'default';
|
};
|
|
zrUtil.mixin(HandlerDomProxy, Eventful);
|
|
module.exports = HandlerDomProxy;
|
|
|
/***/ },
|
/* 91 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Only implements needed gestures for mobile.
|
*/
|
|
|
var eventUtil = __webpack_require__(88);
|
|
var GestureMgr = function () {
|
|
/**
|
* @private
|
* @type {Array.<Object>}
|
*/
|
this._track = [];
|
};
|
|
GestureMgr.prototype = {
|
|
constructor: GestureMgr,
|
|
recognize: function (event, target, root) {
|
this._doTrack(event, target, root);
|
return this._recognize(event);
|
},
|
|
clear: function () {
|
this._track.length = 0;
|
return this;
|
},
|
|
_doTrack: function (event, target, root) {
|
var touches = event.touches;
|
|
if (!touches) {
|
return;
|
}
|
|
var trackItem = {
|
points: [],
|
touches: [],
|
target: target,
|
event: event
|
};
|
|
for (var i = 0, len = touches.length; i < len; i++) {
|
var touch = touches[i];
|
var pos = eventUtil.clientToLocal(root, touch, {});
|
trackItem.points.push([pos.zrX, pos.zrY]);
|
trackItem.touches.push(touch);
|
}
|
|
this._track.push(trackItem);
|
},
|
|
_recognize: function (event) {
|
for (var eventName in recognizers) {
|
if (recognizers.hasOwnProperty(eventName)) {
|
var gestureInfo = recognizers[eventName](this._track, event);
|
if (gestureInfo) {
|
return gestureInfo;
|
}
|
}
|
}
|
}
|
};
|
|
function dist(pointPair) {
|
var dx = pointPair[1][0] - pointPair[0][0];
|
var dy = pointPair[1][1] - pointPair[0][1];
|
|
return Math.sqrt(dx * dx + dy * dy);
|
}
|
|
function center(pointPair) {
|
return [
|
(pointPair[0][0] + pointPair[1][0]) / 2,
|
(pointPair[0][1] + pointPair[1][1]) / 2
|
];
|
}
|
|
var recognizers = {
|
|
pinch: function (track, event) {
|
var trackLen = track.length;
|
|
if (!trackLen) {
|
return;
|
}
|
|
var pinchEnd = (track[trackLen - 1] || {}).points;
|
var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd;
|
|
if (pinchPre
|
&& pinchPre.length > 1
|
&& pinchEnd
|
&& pinchEnd.length > 1
|
) {
|
var pinchScale = dist(pinchEnd) / dist(pinchPre);
|
!isFinite(pinchScale) && (pinchScale = 1);
|
|
event.pinchScale = pinchScale;
|
|
var pinchCenter = center(pinchEnd);
|
event.pinchX = pinchCenter[0];
|
event.pinchY = pinchCenter[1];
|
|
return {
|
type: 'pinch',
|
target: track[0].target,
|
event: event
|
};
|
}
|
}
|
|
// Only pinch currently.
|
};
|
|
module.exports = GestureMgr;
|
|
|
|
/***/ },
|
/* 92 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Default canvas painter
|
* @module zrender/Painter
|
* @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
|
* errorrik (errorrik@gmail.com)
|
* pissang (https://www.github.com/pissang)
|
*/
|
|
|
var config = __webpack_require__(42);
|
var util = __webpack_require__(4);
|
var log = __webpack_require__(41);
|
var BoundingRect = __webpack_require__(9);
|
var timsort = __webpack_require__(86);
|
|
var Layer = __webpack_require__(93);
|
|
var requestAnimationFrame = __webpack_require__(89);
|
|
// PENDIGN
|
// Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time.
|
//
|
// Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer.
|
var MAX_PROGRESSIVE_LAYER_NUMBER = 5;
|
|
function parseInt10(val) {
|
return parseInt(val, 10);
|
}
|
|
function isLayerValid(layer) {
|
if (!layer) {
|
return false;
|
}
|
|
if (layer.__builtin__) {
|
return true;
|
}
|
|
if (typeof(layer.resize) !== 'function'
|
|| typeof(layer.refresh) !== 'function'
|
) {
|
return false;
|
}
|
|
return true;
|
}
|
|
function preProcessLayer(layer) {
|
layer.__unusedCount++;
|
}
|
|
function postProcessLayer(layer) {
|
if (layer.__unusedCount == 1) {
|
layer.clear();
|
}
|
}
|
|
var tmpRect = new BoundingRect(0, 0, 0, 0);
|
var viewRect = new BoundingRect(0, 0, 0, 0);
|
function isDisplayableCulled(el, width, height) {
|
tmpRect.copy(el.getBoundingRect());
|
if (el.transform) {
|
tmpRect.applyTransform(el.transform);
|
}
|
viewRect.width = width;
|
viewRect.height = height;
|
return !tmpRect.intersect(viewRect);
|
}
|
|
function isClipPathChanged(clipPaths, prevClipPaths) {
|
if (clipPaths == prevClipPaths) { // Can both be null or undefined
|
return false;
|
}
|
|
if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) {
|
return true;
|
}
|
for (var i = 0; i < clipPaths.length; i++) {
|
if (clipPaths[i] !== prevClipPaths[i]) {
|
return true;
|
}
|
}
|
}
|
|
function doClip(clipPaths, ctx) {
|
for (var i = 0; i < clipPaths.length; i++) {
|
var clipPath = clipPaths[i];
|
|
clipPath.setTransform(ctx);
|
ctx.beginPath();
|
clipPath.buildPath(ctx, clipPath.shape);
|
ctx.clip();
|
// Transform back
|
clipPath.restoreTransform(ctx);
|
}
|
}
|
|
function createRoot(width, height) {
|
var domRoot = document.createElement('div');
|
|
// domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬
|
domRoot.style.cssText = [
|
'position:relative',
|
'overflow:hidden',
|
'width:' + width + 'px',
|
'height:' + height + 'px',
|
'padding:0',
|
'margin:0',
|
'border-width:0'
|
].join(';') + ';';
|
|
return domRoot;
|
}
|
|
/**
|
* @alias module:zrender/Painter
|
* @constructor
|
* @param {HTMLElement} root 绘图容器
|
* @param {module:zrender/Storage} storage
|
* @param {Ojbect} opts
|
*/
|
var Painter = function (root, storage, opts) {
|
// In node environment using node-canvas
|
var singleCanvas = !root.nodeName // In node ?
|
|| root.nodeName.toUpperCase() === 'CANVAS';
|
|
this._opts = opts = util.extend({}, opts || {});
|
|
/**
|
* @type {number}
|
*/
|
this.dpr = opts.devicePixelRatio || config.devicePixelRatio;
|
/**
|
* @type {boolean}
|
* @private
|
*/
|
this._singleCanvas = singleCanvas;
|
/**
|
* 绘图容器
|
* @type {HTMLElement}
|
*/
|
this.root = root;
|
|
var rootStyle = root.style;
|
|
if (rootStyle) {
|
rootStyle['-webkit-tap-highlight-color'] = 'transparent';
|
rootStyle['-webkit-user-select'] =
|
rootStyle['user-select'] =
|
rootStyle['-webkit-touch-callout'] = 'none';
|
|
root.innerHTML = '';
|
}
|
|
/**
|
* @type {module:zrender/Storage}
|
*/
|
this.storage = storage;
|
|
/**
|
* @type {Array.<number>}
|
* @private
|
*/
|
var zlevelList = this._zlevelList = [];
|
|
/**
|
* @type {Object.<string, module:zrender/Layer>}
|
* @private
|
*/
|
var layers = this._layers = {};
|
|
/**
|
* @type {Object.<string, Object>}
|
* @type {private}
|
*/
|
this._layerConfig = {};
|
|
if (!singleCanvas) {
|
this._width = this._getSize(0);
|
this._height = this._getSize(1);
|
|
var domRoot = this._domRoot = createRoot(
|
this._width, this._height
|
);
|
root.appendChild(domRoot);
|
}
|
else {
|
if (opts.width != null) {
|
root.width = opts.width;
|
}
|
if (opts.height != null) {
|
root.height = opts.height;
|
}
|
// Use canvas width and height directly
|
var width = root.width;
|
var height = root.height;
|
this._width = width;
|
this._height = height;
|
|
// Create layer if only one given canvas
|
// Device pixel ratio is fixed to 1 because given canvas has its specified width and height
|
var mainLayer = new Layer(root, this, 1);
|
mainLayer.initContext();
|
// FIXME Use canvas width and height
|
// mainLayer.resize(width, height);
|
layers[0] = mainLayer;
|
zlevelList.push(0);
|
|
this._domRoot = root;
|
}
|
|
// Layers for progressive rendering
|
this._progressiveLayers = [];
|
|
/**
|
* @type {module:zrender/Layer}
|
* @private
|
*/
|
this._hoverlayer;
|
|
this._hoverElements = [];
|
};
|
|
Painter.prototype = {
|
|
constructor: Painter,
|
|
/**
|
* If painter use a single canvas
|
* @return {boolean}
|
*/
|
isSingleCanvas: function () {
|
return this._singleCanvas;
|
},
|
/**
|
* @return {HTMLDivElement}
|
*/
|
getViewportRoot: function () {
|
return this._domRoot;
|
},
|
|
/**
|
* 刷新
|
* @param {boolean} [paintAll=false] 强制绘制所有displayable
|
*/
|
refresh: function (paintAll) {
|
|
var list = this.storage.getDisplayList(true);
|
|
var zlevelList = this._zlevelList;
|
|
this._paintList(list, paintAll);
|
|
// Paint custum layers
|
for (var i = 0; i < zlevelList.length; i++) {
|
var z = zlevelList[i];
|
var layer = this._layers[z];
|
if (!layer.__builtin__ && layer.refresh) {
|
layer.refresh();
|
}
|
}
|
|
this.refreshHover();
|
|
if (this._progressiveLayers.length) {
|
this._startProgessive();
|
}
|
|
return this;
|
},
|
|
addHover: function (el, hoverStyle) {
|
if (el.__hoverMir) {
|
return;
|
}
|
var elMirror = new el.constructor({
|
style: el.style,
|
shape: el.shape
|
});
|
elMirror.__from = el;
|
el.__hoverMir = elMirror;
|
elMirror.setStyle(hoverStyle);
|
this._hoverElements.push(elMirror);
|
},
|
|
removeHover: function (el) {
|
var elMirror = el.__hoverMir;
|
var hoverElements = this._hoverElements;
|
var idx = util.indexOf(hoverElements, elMirror);
|
if (idx >= 0) {
|
hoverElements.splice(idx, 1);
|
}
|
el.__hoverMir = null;
|
},
|
|
clearHover: function (el) {
|
var hoverElements = this._hoverElements;
|
for (var i = 0; i < hoverElements.length; i++) {
|
var from = hoverElements[i].__from;
|
if (from) {
|
from.__hoverMir = null;
|
}
|
}
|
hoverElements.length = 0;
|
},
|
|
refreshHover: function () {
|
var hoverElements = this._hoverElements;
|
var len = hoverElements.length;
|
var hoverLayer = this._hoverlayer;
|
hoverLayer && hoverLayer.clear();
|
|
if (!len) {
|
return;
|
}
|
timsort(hoverElements, this.storage.displayableSortFunc);
|
|
// Use a extream large zlevel
|
// FIXME?
|
if (!hoverLayer) {
|
hoverLayer = this._hoverlayer = this.getLayer(1e5);
|
}
|
|
var scope = {};
|
hoverLayer.ctx.save();
|
for (var i = 0; i < len;) {
|
var el = hoverElements[i];
|
var originalEl = el.__from;
|
// Original el is removed
|
// PENDING
|
if (!(originalEl && originalEl.__zr)) {
|
hoverElements.splice(i, 1);
|
originalEl.__hoverMir = null;
|
len--;
|
continue;
|
}
|
i++;
|
|
// Use transform
|
// FIXME style and shape ?
|
if (!originalEl.invisible) {
|
el.transform = originalEl.transform;
|
el.invTransform = originalEl.invTransform;
|
el.__clipPaths = originalEl.__clipPaths;
|
// el.
|
this._doPaintEl(el, hoverLayer, true, scope);
|
}
|
}
|
hoverLayer.ctx.restore();
|
},
|
|
_startProgessive: function () {
|
var self = this;
|
|
if (!self._furtherProgressive) {
|
return;
|
}
|
|
// Use a token to stop progress steps triggered by
|
// previous zr.refresh calling.
|
var token = self._progressiveToken = +new Date();
|
|
self._progress++;
|
requestAnimationFrame(step);
|
|
function step() {
|
// In case refreshed or disposed
|
if (token === self._progressiveToken && self.storage) {
|
|
self._doPaintList(self.storage.getDisplayList());
|
|
if (self._furtherProgressive) {
|
self._progress++;
|
requestAnimationFrame(step);
|
}
|
else {
|
self._progressiveToken = -1;
|
}
|
}
|
}
|
},
|
|
_clearProgressive: function () {
|
this._progressiveToken = -1;
|
this._progress = 0;
|
util.each(this._progressiveLayers, function (layer) {
|
layer.__dirty && layer.clear();
|
});
|
},
|
|
_paintList: function (list, paintAll) {
|
|
if (paintAll == null) {
|
paintAll = false;
|
}
|
|
this._updateLayerStatus(list);
|
|
this._clearProgressive();
|
|
this.eachBuiltinLayer(preProcessLayer);
|
|
this._doPaintList(list, paintAll);
|
|
this.eachBuiltinLayer(postProcessLayer);
|
},
|
|
_doPaintList: function (list, paintAll) {
|
var currentLayer;
|
var currentZLevel;
|
var ctx;
|
|
// var invTransform = [];
|
var scope;
|
|
var progressiveLayerIdx = 0;
|
var currentProgressiveLayer;
|
|
var width = this._width;
|
var height = this._height;
|
var layerProgress;
|
var frame = this._progress;
|
function flushProgressiveLayer(layer) {
|
var dpr = ctx.dpr || 1;
|
ctx.save();
|
ctx.globalAlpha = 1;
|
ctx.shadowBlur = 0;
|
// Avoid layer don't clear in next progressive frame
|
currentLayer.__dirty = true;
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr);
|
ctx.restore();
|
}
|
|
for (var i = 0, l = list.length; i < l; i++) {
|
var el = list[i];
|
var elZLevel = this._singleCanvas ? 0 : el.zlevel;
|
|
var elFrame = el.__frame;
|
|
// Flush at current context
|
// PENDING
|
if (elFrame < 0 && currentProgressiveLayer) {
|
flushProgressiveLayer(currentProgressiveLayer);
|
currentProgressiveLayer = null;
|
}
|
|
// Change draw layer
|
if (currentZLevel !== elZLevel) {
|
if (ctx) {
|
ctx.restore();
|
}
|
|
// Reset scope
|
scope = {};
|
|
// Only 0 zlevel if only has one canvas
|
currentZLevel = elZLevel;
|
currentLayer = this.getLayer(currentZLevel);
|
|
if (!currentLayer.__builtin__) {
|
log(
|
'ZLevel ' + currentZLevel
|
+ ' has been used by unkown layer ' + currentLayer.id
|
);
|
}
|
|
ctx = currentLayer.ctx;
|
ctx.save();
|
|
// Reset the count
|
currentLayer.__unusedCount = 0;
|
|
if (currentLayer.__dirty || paintAll) {
|
currentLayer.clear();
|
}
|
}
|
|
if (!(currentLayer.__dirty || paintAll)) {
|
continue;
|
}
|
|
if (elFrame >= 0) {
|
// Progressive layer changed
|
if (!currentProgressiveLayer) {
|
currentProgressiveLayer = this._progressiveLayers[
|
Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1)
|
];
|
|
currentProgressiveLayer.ctx.save();
|
currentProgressiveLayer.renderScope = {};
|
|
if (currentProgressiveLayer
|
&& (currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress)
|
) {
|
// flushProgressiveLayer(currentProgressiveLayer);
|
// Quick jump all progressive elements
|
// All progressive element are not dirty, jump over and flush directly
|
i = currentProgressiveLayer.__nextIdxNotProg - 1;
|
// currentProgressiveLayer = null;
|
continue;
|
}
|
|
layerProgress = currentProgressiveLayer.__progress;
|
|
if (!currentProgressiveLayer.__dirty) {
|
// Keep rendering
|
frame = layerProgress;
|
}
|
|
currentProgressiveLayer.__progress = frame + 1;
|
}
|
|
if (elFrame === frame) {
|
this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope);
|
}
|
}
|
else {
|
this._doPaintEl(el, currentLayer, paintAll, scope);
|
}
|
|
el.__dirty = false;
|
}
|
|
if (currentProgressiveLayer) {
|
flushProgressiveLayer(currentProgressiveLayer);
|
}
|
|
// Restore the lastLayer ctx
|
ctx && ctx.restore();
|
// If still has clipping state
|
// if (scope.prevElClipPaths) {
|
// ctx.restore();
|
// }
|
|
this._furtherProgressive = false;
|
util.each(this._progressiveLayers, function (layer) {
|
if (layer.__maxProgress >= layer.__progress) {
|
this._furtherProgressive = true;
|
}
|
}, this);
|
},
|
|
_doPaintEl: function (el, currentLayer, forcePaint, scope) {
|
var ctx = currentLayer.ctx;
|
var m = el.transform;
|
if (
|
(currentLayer.__dirty || forcePaint)
|
// Ignore invisible element
|
&& !el.invisible
|
// Ignore transparent element
|
&& el.style.opacity !== 0
|
// Ignore scale 0 element, in some environment like node-canvas
|
// Draw a scale 0 element can cause all following draw wrong
|
// And setTransform with scale 0 will cause set back transform failed.
|
&& !(m && !m[0] && !m[3])
|
// Ignore culled element
|
&& !(el.culling && isDisplayableCulled(el, this._width, this._height))
|
) {
|
|
var clipPaths = el.__clipPaths;
|
|
// Optimize when clipping on group with several elements
|
if (scope.prevClipLayer !== currentLayer
|
|| isClipPathChanged(clipPaths, scope.prevElClipPaths)
|
) {
|
// If has previous clipping state, restore from it
|
if (scope.prevElClipPaths) {
|
scope.prevClipLayer.ctx.restore();
|
scope.prevClipLayer = scope.prevElClipPaths = null;
|
|
// Reset prevEl since context has been restored
|
scope.prevEl = null;
|
}
|
// New clipping state
|
if (clipPaths) {
|
ctx.save();
|
doClip(clipPaths, ctx);
|
scope.prevClipLayer = currentLayer;
|
scope.prevElClipPaths = clipPaths;
|
}
|
}
|
el.beforeBrush && el.beforeBrush(ctx);
|
|
el.brush(ctx, scope.prevEl || null);
|
scope.prevEl = el;
|
|
el.afterBrush && el.afterBrush(ctx);
|
}
|
},
|
|
/**
|
* 获取 zlevel 所在层,如果不存在则会创建一个新的层
|
* @param {number} zlevel
|
* @return {module:zrender/Layer}
|
*/
|
getLayer: function (zlevel) {
|
if (this._singleCanvas) {
|
return this._layers[0];
|
}
|
|
var layer = this._layers[zlevel];
|
if (!layer) {
|
// Create a new layer
|
layer = new Layer('zr_' + zlevel, this, this.dpr);
|
layer.__builtin__ = true;
|
|
if (this._layerConfig[zlevel]) {
|
util.merge(layer, this._layerConfig[zlevel], true);
|
}
|
|
this.insertLayer(zlevel, layer);
|
|
// Context is created after dom inserted to document
|
// Or excanvas will get 0px clientWidth and clientHeight
|
layer.initContext();
|
}
|
|
return layer;
|
},
|
|
insertLayer: function (zlevel, layer) {
|
|
var layersMap = this._layers;
|
var zlevelList = this._zlevelList;
|
var len = zlevelList.length;
|
var prevLayer = null;
|
var i = -1;
|
var domRoot = this._domRoot;
|
|
if (layersMap[zlevel]) {
|
log('ZLevel ' + zlevel + ' has been used already');
|
return;
|
}
|
// Check if is a valid layer
|
if (!isLayerValid(layer)) {
|
log('Layer of zlevel ' + zlevel + ' is not valid');
|
return;
|
}
|
|
if (len > 0 && zlevel > zlevelList[0]) {
|
for (i = 0; i < len - 1; i++) {
|
if (
|
zlevelList[i] < zlevel
|
&& zlevelList[i + 1] > zlevel
|
) {
|
break;
|
}
|
}
|
prevLayer = layersMap[zlevelList[i]];
|
}
|
zlevelList.splice(i + 1, 0, zlevel);
|
|
layersMap[zlevel] = layer;
|
|
// Vitual layer will not directly show on the screen.
|
// (It can be a WebGL layer and assigned to a ZImage element)
|
// But it still under management of zrender.
|
if (!layer.virtual) {
|
if (prevLayer) {
|
var prevDom = prevLayer.dom;
|
if (prevDom.nextSibling) {
|
domRoot.insertBefore(
|
layer.dom,
|
prevDom.nextSibling
|
);
|
}
|
else {
|
domRoot.appendChild(layer.dom);
|
}
|
}
|
else {
|
if (domRoot.firstChild) {
|
domRoot.insertBefore(layer.dom, domRoot.firstChild);
|
}
|
else {
|
domRoot.appendChild(layer.dom);
|
}
|
}
|
}
|
},
|
|
// Iterate each layer
|
eachLayer: function (cb, context) {
|
var zlevelList = this._zlevelList;
|
var z;
|
var i;
|
for (i = 0; i < zlevelList.length; i++) {
|
z = zlevelList[i];
|
cb.call(context, this._layers[z], z);
|
}
|
},
|
|
// Iterate each buildin layer
|
eachBuiltinLayer: function (cb, context) {
|
var zlevelList = this._zlevelList;
|
var layer;
|
var z;
|
var i;
|
for (i = 0; i < zlevelList.length; i++) {
|
z = zlevelList[i];
|
layer = this._layers[z];
|
if (layer.__builtin__) {
|
cb.call(context, layer, z);
|
}
|
}
|
},
|
|
// Iterate each other layer except buildin layer
|
eachOtherLayer: function (cb, context) {
|
var zlevelList = this._zlevelList;
|
var layer;
|
var z;
|
var i;
|
for (i = 0; i < zlevelList.length; i++) {
|
z = zlevelList[i];
|
layer = this._layers[z];
|
if (!layer.__builtin__) {
|
cb.call(context, layer, z);
|
}
|
}
|
},
|
|
/**
|
* 获取所有已创建的层
|
* @param {Array.<module:zrender/Layer>} [prevLayer]
|
*/
|
getLayers: function () {
|
return this._layers;
|
},
|
|
_updateLayerStatus: function (list) {
|
|
var layers = this._layers;
|
var progressiveLayers = this._progressiveLayers;
|
|
var elCountsLastFrame = {};
|
var progressiveElCountsLastFrame = {};
|
|
this.eachBuiltinLayer(function (layer, z) {
|
elCountsLastFrame[z] = layer.elCount;
|
layer.elCount = 0;
|
layer.__dirty = false;
|
});
|
|
util.each(progressiveLayers, function (layer, idx) {
|
progressiveElCountsLastFrame[idx] = layer.elCount;
|
layer.elCount = 0;
|
layer.__dirty = false;
|
});
|
|
var progressiveLayerCount = 0;
|
var currentProgressiveLayer;
|
var lastProgressiveKey;
|
var frameCount = 0;
|
for (var i = 0, l = list.length; i < l; i++) {
|
var el = list[i];
|
var zlevel = this._singleCanvas ? 0 : el.zlevel;
|
var layer = layers[zlevel];
|
var elProgress = el.progressive;
|
if (layer) {
|
layer.elCount++;
|
layer.__dirty = layer.__dirty || el.__dirty;
|
}
|
|
/////// Update progressive
|
if (elProgress >= 0) {
|
// Fix wrong progressive sequence problem.
|
if (lastProgressiveKey !== elProgress) {
|
lastProgressiveKey = elProgress;
|
frameCount++;
|
}
|
var elFrame = el.__frame = frameCount - 1;
|
if (!currentProgressiveLayer) {
|
var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1);
|
currentProgressiveLayer = progressiveLayers[idx];
|
if (!currentProgressiveLayer) {
|
currentProgressiveLayer = progressiveLayers[idx] = new Layer(
|
'progressive', this, this.dpr
|
);
|
currentProgressiveLayer.initContext();
|
}
|
currentProgressiveLayer.__maxProgress = 0;
|
}
|
currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty;
|
currentProgressiveLayer.elCount++;
|
|
currentProgressiveLayer.__maxProgress = Math.max(
|
currentProgressiveLayer.__maxProgress, elFrame
|
);
|
|
if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) {
|
// Should keep rendering this layer because progressive rendering is not finished yet
|
layer.__dirty = true;
|
}
|
}
|
else {
|
el.__frame = -1;
|
|
if (currentProgressiveLayer) {
|
currentProgressiveLayer.__nextIdxNotProg = i;
|
progressiveLayerCount++;
|
currentProgressiveLayer = null;
|
}
|
}
|
}
|
|
if (currentProgressiveLayer) {
|
progressiveLayerCount++;
|
currentProgressiveLayer.__nextIdxNotProg = i;
|
}
|
|
// 层中的元素数量有发生变化
|
this.eachBuiltinLayer(function (layer, z) {
|
if (elCountsLastFrame[z] !== layer.elCount) {
|
layer.__dirty = true;
|
}
|
});
|
|
progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER);
|
util.each(progressiveLayers, function (layer, idx) {
|
if (progressiveElCountsLastFrame[idx] !== layer.elCount) {
|
el.__dirty = true;
|
}
|
if (layer.__dirty) {
|
layer.__progress = 0;
|
}
|
});
|
},
|
|
/**
|
* 清除hover层外所有内容
|
*/
|
clear: function () {
|
this.eachBuiltinLayer(this._clearLayer);
|
return this;
|
},
|
|
_clearLayer: function (layer) {
|
layer.clear();
|
},
|
|
/**
|
* 修改指定zlevel的绘制参数
|
*
|
* @param {string} zlevel
|
* @param {Object} config 配置对象
|
* @param {string} [config.clearColor=0] 每次清空画布的颜色
|
* @param {string} [config.motionBlur=false] 是否开启动态模糊
|
* @param {number} [config.lastFrameAlpha=0.7]
|
* 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
|
*/
|
configLayer: function (zlevel, config) {
|
if (config) {
|
var layerConfig = this._layerConfig;
|
if (!layerConfig[zlevel]) {
|
layerConfig[zlevel] = config;
|
}
|
else {
|
util.merge(layerConfig[zlevel], config, true);
|
}
|
|
var layer = this._layers[zlevel];
|
|
if (layer) {
|
util.merge(layer, layerConfig[zlevel], true);
|
}
|
}
|
},
|
|
/**
|
* 删除指定层
|
* @param {number} zlevel 层所在的zlevel
|
*/
|
delLayer: function (zlevel) {
|
var layers = this._layers;
|
var zlevelList = this._zlevelList;
|
var layer = layers[zlevel];
|
if (!layer) {
|
return;
|
}
|
layer.dom.parentNode.removeChild(layer.dom);
|
delete layers[zlevel];
|
|
zlevelList.splice(util.indexOf(zlevelList, zlevel), 1);
|
},
|
|
/**
|
* 区域大小变化后重绘
|
*/
|
resize: function (width, height) {
|
var domRoot = this._domRoot;
|
// FIXME Why ?
|
domRoot.style.display = 'none';
|
|
// Save input w/h
|
var opts = this._opts;
|
width != null && (opts.width = width);
|
height != null && (opts.height = height);
|
|
width = this._getSize(0);
|
height = this._getSize(1);
|
|
domRoot.style.display = '';
|
|
// 优化没有实际改变的resize
|
if (this._width != width || height != this._height) {
|
domRoot.style.width = width + 'px';
|
domRoot.style.height = height + 'px';
|
|
for (var id in this._layers) {
|
if (this._layers.hasOwnProperty(id)) {
|
this._layers[id].resize(width, height);
|
}
|
}
|
util.each(this._progressiveLayers, function (layer) {
|
layer.resize(width, height);
|
});
|
|
this.refresh(true);
|
}
|
|
this._width = width;
|
this._height = height;
|
|
return this;
|
},
|
|
/**
|
* 清除单独的一个层
|
* @param {number} zlevel
|
*/
|
clearLayer: function (zlevel) {
|
var layer = this._layers[zlevel];
|
if (layer) {
|
layer.clear();
|
}
|
},
|
|
/**
|
* 释放
|
*/
|
dispose: function () {
|
this.root.innerHTML = '';
|
|
this.root =
|
this.storage =
|
|
this._domRoot =
|
this._layers = null;
|
},
|
|
/**
|
* Get canvas which has all thing rendered
|
* @param {Object} opts
|
* @param {string} [opts.backgroundColor]
|
*/
|
getRenderedCanvas: function (opts) {
|
opts = opts || {};
|
if (this._singleCanvas) {
|
return this._layers[0].dom;
|
}
|
|
var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr);
|
imageLayer.initContext();
|
|
imageLayer.clearColor = opts.backgroundColor;
|
imageLayer.clear();
|
|
var displayList = this.storage.getDisplayList(true);
|
|
var scope = {};
|
for (var i = 0; i < displayList.length; i++) {
|
var el = displayList[i];
|
this._doPaintEl(el, imageLayer, true, scope);
|
}
|
|
return imageLayer.dom;
|
},
|
/**
|
* 获取绘图区域宽度
|
*/
|
getWidth: function () {
|
return this._width;
|
},
|
|
/**
|
* 获取绘图区域高度
|
*/
|
getHeight: function () {
|
return this._height;
|
},
|
|
_getSize: function (whIdx) {
|
var opts = this._opts;
|
var wh = ['width', 'height'][whIdx];
|
var cwh = ['clientWidth', 'clientHeight'][whIdx];
|
var plt = ['paddingLeft', 'paddingTop'][whIdx];
|
var prb = ['paddingRight', 'paddingBottom'][whIdx];
|
|
if (opts[wh] != null && opts[wh] !== 'auto') {
|
return parseFloat(opts[wh]);
|
}
|
|
var root = this.root;
|
var stl = document.defaultView.getComputedStyle(root);
|
|
return (
|
(root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh]))
|
- (parseInt10(stl[plt]) || 0)
|
- (parseInt10(stl[prb]) || 0)
|
) | 0;
|
},
|
|
pathToImage: function (path, dpr) {
|
dpr = dpr || this.dpr;
|
|
var canvas = document.createElement('canvas');
|
var ctx = canvas.getContext('2d');
|
var rect = path.getBoundingRect();
|
var style = path.style;
|
var shadowBlurSize = style.shadowBlur;
|
var shadowOffsetX = style.shadowOffsetX;
|
var shadowOffsetY = style.shadowOffsetY;
|
var lineWidth = style.hasStroke() ? style.lineWidth : 0;
|
|
var leftMargin = Math.max(lineWidth / 2, -shadowOffsetX + shadowBlurSize);
|
var rightMargin = Math.max(lineWidth / 2, shadowOffsetX + shadowBlurSize);
|
var topMargin = Math.max(lineWidth / 2, -shadowOffsetY + shadowBlurSize);
|
var bottomMargin = Math.max(lineWidth / 2, shadowOffsetY + shadowBlurSize);
|
var width = rect.width + leftMargin + rightMargin;
|
var height = rect.height + topMargin + bottomMargin;
|
|
canvas.width = width * dpr;
|
canvas.height = height * dpr;
|
|
ctx.scale(dpr, dpr);
|
ctx.clearRect(0, 0, width, height);
|
ctx.dpr = dpr;
|
|
var pathTransform = {
|
position: path.position,
|
rotation: path.rotation,
|
scale: path.scale
|
};
|
path.position = [leftMargin - rect.x, topMargin - rect.y];
|
path.rotation = 0;
|
path.scale = [1, 1];
|
path.updateTransform();
|
if (path) {
|
path.brush(ctx);
|
}
|
|
var ImageShape = __webpack_require__(62);
|
var imgShape = new ImageShape({
|
style: {
|
x: 0,
|
y: 0,
|
image: canvas
|
}
|
});
|
|
if (pathTransform.position != null) {
|
imgShape.position = path.position = pathTransform.position;
|
}
|
|
if (pathTransform.rotation != null) {
|
imgShape.rotation = path.rotation = pathTransform.rotation;
|
}
|
|
if (pathTransform.scale != null) {
|
imgShape.scale = path.scale = pathTransform.scale;
|
}
|
|
return imgShape;
|
}
|
};
|
|
module.exports = Painter;
|
|
|
|
/***/ },
|
/* 93 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module zrender/Layer
|
* @author pissang(https://www.github.com/pissang)
|
*/
|
|
|
var util = __webpack_require__(4);
|
var config = __webpack_require__(42);
|
var Style = __webpack_require__(48);
|
var Pattern = __webpack_require__(60);
|
|
function returnFalse() {
|
return false;
|
}
|
|
/**
|
* 创建dom
|
*
|
* @inner
|
* @param {string} id dom id 待用
|
* @param {string} type dom type,such as canvas, div etc.
|
* @param {Painter} painter painter instance
|
* @param {number} number
|
*/
|
function createDom(id, type, painter, dpr) {
|
var newDom = document.createElement(type);
|
var width = painter.getWidth();
|
var height = painter.getHeight();
|
|
var newDomStyle = newDom.style;
|
// 没append呢,请原谅我这样写,清晰~
|
newDomStyle.position = 'absolute';
|
newDomStyle.left = 0;
|
newDomStyle.top = 0;
|
newDomStyle.width = width + 'px';
|
newDomStyle.height = height + 'px';
|
newDom.width = width * dpr;
|
newDom.height = height * dpr;
|
|
// id不作为索引用,避免可能造成的重名,定义为私有属性
|
newDom.setAttribute('data-zr-dom-id', id);
|
return newDom;
|
}
|
|
/**
|
* @alias module:zrender/Layer
|
* @constructor
|
* @extends module:zrender/mixin/Transformable
|
* @param {string} id
|
* @param {module:zrender/Painter} painter
|
* @param {number} [dpr]
|
*/
|
var Layer = function(id, painter, dpr) {
|
var dom;
|
dpr = dpr || config.devicePixelRatio;
|
if (typeof id === 'string') {
|
dom = createDom(id, 'canvas', painter, dpr);
|
}
|
// Not using isDom because in node it will return false
|
else if (util.isObject(id)) {
|
dom = id;
|
id = dom.id;
|
}
|
this.id = id;
|
this.dom = dom;
|
|
var domStyle = dom.style;
|
if (domStyle) { // Not in node
|
dom.onselectstart = returnFalse; // 避免页面选中的尴尬
|
domStyle['-webkit-user-select'] = 'none';
|
domStyle['user-select'] = 'none';
|
domStyle['-webkit-touch-callout'] = 'none';
|
domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)';
|
domStyle['padding'] = 0;
|
domStyle['margin'] = 0;
|
domStyle['border-width'] = 0;
|
}
|
|
this.domBack = null;
|
this.ctxBack = null;
|
|
this.painter = painter;
|
|
this.config = null;
|
|
// Configs
|
/**
|
* 每次清空画布的颜色
|
* @type {string}
|
* @default 0
|
*/
|
this.clearColor = 0;
|
/**
|
* 是否开启动态模糊
|
* @type {boolean}
|
* @default false
|
*/
|
this.motionBlur = false;
|
/**
|
* 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显
|
* @type {number}
|
* @default 0.7
|
*/
|
this.lastFrameAlpha = 0.7;
|
|
/**
|
* Layer dpr
|
* @type {number}
|
*/
|
this.dpr = dpr;
|
};
|
|
Layer.prototype = {
|
|
constructor: Layer,
|
|
elCount: 0,
|
|
__dirty: true,
|
|
initContext: function () {
|
this.ctx = this.dom.getContext('2d');
|
|
this.ctx.dpr = this.dpr;
|
},
|
|
createBackBuffer: function () {
|
var dpr = this.dpr;
|
|
this.domBack = createDom('back-' + this.id, 'canvas', this.painter, dpr);
|
this.ctxBack = this.domBack.getContext('2d');
|
|
if (dpr != 1) {
|
this.ctxBack.scale(dpr, dpr);
|
}
|
},
|
|
/**
|
* @param {number} width
|
* @param {number} height
|
*/
|
resize: function (width, height) {
|
var dpr = this.dpr;
|
|
var dom = this.dom;
|
var domStyle = dom.style;
|
var domBack = this.domBack;
|
|
domStyle.width = width + 'px';
|
domStyle.height = height + 'px';
|
|
dom.width = width * dpr;
|
dom.height = height * dpr;
|
|
if (domBack) {
|
domBack.width = width * dpr;
|
domBack.height = height * dpr;
|
|
if (dpr != 1) {
|
this.ctxBack.scale(dpr, dpr);
|
}
|
}
|
},
|
|
/**
|
* 清空该层画布
|
* @param {boolean} clearAll Clear all with out motion blur
|
*/
|
clear: function (clearAll) {
|
var dom = this.dom;
|
var ctx = this.ctx;
|
var width = dom.width;
|
var height = dom.height;
|
|
var clearColor = this.clearColor;
|
var haveMotionBLur = this.motionBlur && !clearAll;
|
var lastFrameAlpha = this.lastFrameAlpha;
|
|
var dpr = this.dpr;
|
|
if (haveMotionBLur) {
|
if (!this.domBack) {
|
this.createBackBuffer();
|
}
|
|
this.ctxBack.globalCompositeOperation = 'copy';
|
this.ctxBack.drawImage(
|
dom, 0, 0,
|
width / dpr,
|
height / dpr
|
);
|
}
|
|
ctx.clearRect(0, 0, width, height);
|
if (clearColor) {
|
var clearColorGradientOrPattern;
|
// Gradient
|
if (clearColor.colorStops) {
|
// Cache canvas gradient
|
clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, {
|
x: 0,
|
y: 0,
|
width: width,
|
height: height
|
});
|
|
clearColor.__canvasGradient = clearColorGradientOrPattern;
|
}
|
// Pattern
|
else if (clearColor.image) {
|
clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx);
|
}
|
ctx.save();
|
ctx.fillStyle = clearColorGradientOrPattern || clearColor;
|
ctx.fillRect(0, 0, width, height);
|
ctx.restore();
|
}
|
|
if (haveMotionBLur) {
|
var domBack = this.domBack;
|
ctx.save();
|
ctx.globalAlpha = lastFrameAlpha;
|
ctx.drawImage(domBack, 0, 0, width, height);
|
ctx.restore();
|
}
|
}
|
};
|
|
module.exports = Layer;
|
|
|
/***/ },
|
/* 94 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var Gradient = __webpack_require__(79);
|
module.exports = function (ecModel) {
|
function encodeColor(seriesModel) {
|
var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.');
|
var data = seriesModel.getData();
|
var color = seriesModel.get(colorAccessPath) // Set in itemStyle
|
|| seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color
|
|
// FIXME Set color function or use the platte color
|
data.setVisual('color', color);
|
|
// Only visible series has each data be visual encoded
|
if (!ecModel.isSeriesFiltered(seriesModel)) {
|
if (typeof color === 'function' && !(color instanceof Gradient)) {
|
data.each(function (idx) {
|
data.setItemVisual(
|
idx, 'color', color(seriesModel.getDataParams(idx))
|
);
|
});
|
}
|
|
// itemStyle in each data item
|
data.each(function (idx) {
|
var itemModel = data.getItemModel(idx);
|
var color = itemModel.get(colorAccessPath, true);
|
if (color != null) {
|
data.setItemVisual(idx, 'color', color);
|
}
|
});
|
}
|
}
|
ecModel.eachRawSeries(encodeColor);
|
};
|
|
|
/***/ },
|
/* 95 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// Compatitable with 2.0
|
|
|
var zrUtil = __webpack_require__(4);
|
var compatStyle = __webpack_require__(96);
|
|
function get(opt, path) {
|
path = path.split(',');
|
var obj = opt;
|
for (var i = 0; i < path.length; i++) {
|
obj = obj && obj[path[i]];
|
if (obj == null) {
|
break;
|
}
|
}
|
return obj;
|
}
|
|
function set(opt, path, val, overwrite) {
|
path = path.split(',');
|
var obj = opt;
|
var key;
|
for (var i = 0; i < path.length - 1; i++) {
|
key = path[i];
|
if (obj[key] == null) {
|
obj[key] = {};
|
}
|
obj = obj[key];
|
}
|
if (overwrite || obj[path[i]] == null) {
|
obj[path[i]] = val;
|
}
|
}
|
|
function compatLayoutProperties(option) {
|
each(LAYOUT_PROPERTIES, function (prop) {
|
if (prop[0] in option && !(prop[1] in option)) {
|
option[prop[1]] = option[prop[0]];
|
}
|
});
|
}
|
|
var LAYOUT_PROPERTIES = [
|
['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']
|
];
|
|
var COMPATITABLE_COMPONENTS = [
|
'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline'
|
];
|
|
var COMPATITABLE_SERIES = [
|
'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter',
|
'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel',
|
'pie', 'radar', 'sankey', 'scatter', 'treemap'
|
];
|
|
var each = zrUtil.each;
|
|
module.exports = function (option) {
|
each(option.series, function (seriesOpt) {
|
if (!zrUtil.isObject(seriesOpt)) {
|
return;
|
}
|
|
var seriesType = seriesOpt.type;
|
|
compatStyle(seriesOpt);
|
|
if (seriesType === 'pie' || seriesType === 'gauge') {
|
if (seriesOpt.clockWise != null) {
|
seriesOpt.clockwise = seriesOpt.clockWise;
|
}
|
}
|
if (seriesType === 'gauge') {
|
var pointerColor = get(seriesOpt, 'pointer.color');
|
pointerColor != null
|
&& set(seriesOpt, 'itemStyle.normal.color', pointerColor);
|
}
|
|
for (var i = 0; i < COMPATITABLE_SERIES.length; i++) {
|
if (COMPATITABLE_SERIES[i] === seriesOpt.type) {
|
compatLayoutProperties(seriesOpt);
|
break;
|
}
|
}
|
});
|
|
// dataRange has changed to visualMap
|
if (option.dataRange) {
|
option.visualMap = option.dataRange;
|
}
|
|
each(COMPATITABLE_COMPONENTS, function (componentName) {
|
var options = option[componentName];
|
if (options) {
|
if (!zrUtil.isArray(options)) {
|
options = [options];
|
}
|
each(options, function (option) {
|
compatLayoutProperties(option);
|
});
|
}
|
});
|
};
|
|
|
/***/ },
|
/* 96 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var POSSIBLE_STYLES = [
|
'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle',
|
'chordStyle', 'label', 'labelLine'
|
];
|
|
function compatItemStyle(opt) {
|
var itemStyleOpt = opt && opt.itemStyle;
|
if (itemStyleOpt) {
|
zrUtil.each(POSSIBLE_STYLES, function (styleName) {
|
var normalItemStyleOpt = itemStyleOpt.normal;
|
var emphasisItemStyleOpt = itemStyleOpt.emphasis;
|
if (normalItemStyleOpt && normalItemStyleOpt[styleName]) {
|
opt[styleName] = opt[styleName] || {};
|
if (!opt[styleName].normal) {
|
opt[styleName].normal = normalItemStyleOpt[styleName];
|
}
|
else {
|
zrUtil.merge(opt[styleName].normal, normalItemStyleOpt[styleName]);
|
}
|
normalItemStyleOpt[styleName] = null;
|
}
|
if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) {
|
opt[styleName] = opt[styleName] || {};
|
if (!opt[styleName].emphasis) {
|
opt[styleName].emphasis = emphasisItemStyleOpt[styleName];
|
}
|
else {
|
zrUtil.merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]);
|
}
|
emphasisItemStyleOpt[styleName] = null;
|
}
|
});
|
}
|
}
|
|
module.exports = function (seriesOpt) {
|
if (!seriesOpt) {
|
return;
|
}
|
compatItemStyle(seriesOpt);
|
compatItemStyle(seriesOpt.markPoint);
|
compatItemStyle(seriesOpt.markLine);
|
var data = seriesOpt.data;
|
if (data) {
|
for (var i = 0; i < data.length; i++) {
|
compatItemStyle(data[i]);
|
}
|
// mark point data
|
var markPoint = seriesOpt.markPoint;
|
if (markPoint && markPoint.data) {
|
var mpData = markPoint.data;
|
for (var i = 0; i < mpData.length; i++) {
|
compatItemStyle(mpData[i]);
|
}
|
}
|
// mark line data
|
var markLine = seriesOpt.markLine;
|
if (markLine && markLine.data) {
|
var mlData = markLine.data;
|
for (var i = 0; i < mlData.length; i++) {
|
if (zrUtil.isArray(mlData[i])) {
|
compatItemStyle(mlData[i][0]);
|
compatItemStyle(mlData[i][1]);
|
}
|
else {
|
compatItemStyle(mlData[i]);
|
}
|
}
|
}
|
}
|
};
|
|
|
/***/ },
|
/* 97 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var graphic = __webpack_require__(44);
|
var zrUtil = __webpack_require__(4);
|
var PI = Math.PI;
|
/**
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} [opts]
|
* @param {string} [opts.text]
|
* @param {string} [opts.color]
|
* @param {string} [opts.textColor]
|
* @return {module:zrender/Element}
|
*/
|
module.exports = function (api, opts) {
|
opts = opts || {};
|
zrUtil.defaults(opts, {
|
text: 'loading',
|
color: '#c23531',
|
textColor: '#000',
|
maskColor: 'rgba(255, 255, 255, 0.8)',
|
zlevel: 0
|
});
|
var mask = new graphic.Rect({
|
style: {
|
fill: opts.maskColor
|
},
|
zlevel: opts.zlevel,
|
z: 10000
|
});
|
var arc = new graphic.Arc({
|
shape: {
|
startAngle: -PI / 2,
|
endAngle: -PI / 2 + 0.1,
|
r: 10
|
},
|
style: {
|
stroke: opts.color,
|
lineCap: 'round',
|
lineWidth: 5
|
},
|
zlevel: opts.zlevel,
|
z: 10001
|
});
|
var labelRect = new graphic.Rect({
|
style: {
|
fill: 'none',
|
text: opts.text,
|
textPosition: 'right',
|
textDistance: 10,
|
textFill: opts.textColor
|
},
|
zlevel: opts.zlevel,
|
z: 10001
|
});
|
|
arc.animateShape(true)
|
.when(1000, {
|
endAngle: PI * 3 / 2
|
})
|
.start('circularInOut');
|
arc.animateShape(true)
|
.when(1000, {
|
startAngle: PI * 3 / 2
|
})
|
.delay(300)
|
.start('circularInOut');
|
|
var group = new graphic.Group();
|
group.add(arc);
|
group.add(labelRect);
|
group.add(mask);
|
// Inject resize
|
group.resize = function () {
|
var cx = api.getWidth() / 2;
|
var cy = api.getHeight() / 2;
|
arc.setShape({
|
cx: cx,
|
cy: cy
|
});
|
var r = arc.shape.r;
|
labelRect.setShape({
|
x: cx - r,
|
y: cy - r,
|
width: r * 2,
|
height: r * 2
|
});
|
|
mask.setShape({
|
x: 0,
|
y: 0,
|
width: api.getWidth(),
|
height: api.getHeight()
|
});
|
};
|
group.resize();
|
return group;
|
};
|
|
|
/***/ },
|
/* 98 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/* WEBPACK VAR INJECTION */(function(global) {/**
|
* List for data storage
|
* @module echarts/data/List
|
*/
|
|
|
var UNDEFINED = 'undefined';
|
var globalObj = typeof window === 'undefined' ? global : window;
|
var Float64Array = typeof globalObj.Float64Array === UNDEFINED
|
? Array : globalObj.Float64Array;
|
var Int32Array = typeof globalObj.Int32Array === UNDEFINED
|
? Array : globalObj.Int32Array;
|
|
var dataCtors = {
|
'float': Float64Array,
|
'int': Int32Array,
|
// Ordinal data type can be string or int
|
'ordinal': Array,
|
'number': Array,
|
'time': Array
|
};
|
|
var Model = __webpack_require__(12);
|
var DataDiffer = __webpack_require__(99);
|
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var isObject = zrUtil.isObject;
|
|
var TRANSFERABLE_PROPERTIES = [
|
'stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'
|
];
|
|
var transferProperties = function (a, b) {
|
zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
|
if (b.hasOwnProperty(propName)) {
|
a[propName] = b[propName];
|
}
|
});
|
|
a.__wrappedMethods = b.__wrappedMethods;
|
};
|
|
/**
|
* @constructor
|
* @alias module:echarts/data/List
|
*
|
* @param {Array.<string|Object>} dimensions
|
* For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
|
* Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
|
* @param {module:echarts/model/Model} hostModel
|
*/
|
var List = function (dimensions, hostModel) {
|
|
dimensions = dimensions || ['x', 'y'];
|
|
var dimensionInfos = {};
|
var dimensionNames = [];
|
for (var i = 0; i < dimensions.length; i++) {
|
var dimensionName;
|
var dimensionInfo = {};
|
if (typeof dimensions[i] === 'string') {
|
dimensionName = dimensions[i];
|
dimensionInfo = {
|
name: dimensionName,
|
stackable: false,
|
// Type can be 'float', 'int', 'number'
|
// Default is number, Precision of float may not enough
|
type: 'number'
|
};
|
}
|
else {
|
dimensionInfo = dimensions[i];
|
dimensionName = dimensionInfo.name;
|
dimensionInfo.type = dimensionInfo.type || 'number';
|
}
|
dimensionNames.push(dimensionName);
|
dimensionInfos[dimensionName] = dimensionInfo;
|
}
|
/**
|
* @readOnly
|
* @type {Array.<string>}
|
*/
|
this.dimensions = dimensionNames;
|
|
/**
|
* Infomation of each data dimension, like data type.
|
* @type {Object}
|
*/
|
this._dimensionInfos = dimensionInfos;
|
|
/**
|
* @type {module:echarts/model/Model}
|
*/
|
this.hostModel = hostModel;
|
|
/**
|
* @type {module:echarts/model/Model}
|
*/
|
this.dataType;
|
|
/**
|
* Indices stores the indices of data subset after filtered.
|
* This data subset will be used in chart.
|
* @type {Array.<number>}
|
* @readOnly
|
*/
|
this.indices = [];
|
|
/**
|
* Data storage
|
* @type {Object.<key, TypedArray|Array>}
|
* @private
|
*/
|
this._storage = {};
|
|
/**
|
* @type {Array.<string>}
|
*/
|
this._nameList = [];
|
/**
|
* @type {Array.<string>}
|
*/
|
this._idList = [];
|
/**
|
* Models of data option is stored sparse for optimizing memory cost
|
* @type {Array.<module:echarts/model/Model>}
|
* @private
|
*/
|
this._optionModels = [];
|
|
/**
|
* @param {module:echarts/data/List}
|
*/
|
this.stackedOn = null;
|
|
/**
|
* Global visual properties after visual coding
|
* @type {Object}
|
* @private
|
*/
|
this._visual = {};
|
|
/**
|
* Globel layout properties.
|
* @type {Object}
|
* @private
|
*/
|
this._layout = {};
|
|
/**
|
* Item visual properties after visual coding
|
* @type {Array.<Object>}
|
* @private
|
*/
|
this._itemVisuals = [];
|
|
/**
|
* Item layout properties after layout
|
* @type {Array.<Object>}
|
* @private
|
*/
|
this._itemLayouts = [];
|
|
/**
|
* Graphic elemnents
|
* @type {Array.<module:zrender/Element>}
|
* @private
|
*/
|
this._graphicEls = [];
|
|
/**
|
* @type {Array.<Array|Object>}
|
* @private
|
*/
|
this._rawData;
|
|
/**
|
* @type {Object}
|
* @private
|
*/
|
this._extent;
|
};
|
|
var listProto = List.prototype;
|
|
listProto.type = 'list';
|
/**
|
* If each data item has it's own option
|
* @type {boolean}
|
*/
|
listProto.hasItemOption = true;
|
|
/**
|
* Get dimension name
|
* @param {string|number} dim
|
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
|
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
|
* @return {string} Concrete dim name.
|
*/
|
listProto.getDimension = function (dim) {
|
if (!isNaN(dim)) {
|
dim = this.dimensions[dim] || dim;
|
}
|
return dim;
|
};
|
/**
|
* Get type and stackable info of particular dimension
|
* @param {string|number} dim
|
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
|
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
|
*/
|
listProto.getDimensionInfo = function (dim) {
|
return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);
|
};
|
|
/**
|
* Initialize from data
|
* @param {Array.<Object|number|Array>} data
|
* @param {Array.<string>} [nameList]
|
* @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
|
*/
|
listProto.initData = function (data, nameList, dimValueGetter) {
|
data = data || [];
|
|
if (true) {
|
if (!zrUtil.isArray(data)) {
|
throw new Error('Invalid data.');
|
}
|
}
|
|
this._rawData = data;
|
|
// Clear
|
var storage = this._storage = {};
|
var indices = this.indices = [];
|
|
var dimensions = this.dimensions;
|
var size = data.length;
|
var dimensionInfoMap = this._dimensionInfos;
|
|
var idList = [];
|
var nameRepeatCount = {};
|
|
nameList = nameList || [];
|
|
// Init storage
|
for (var i = 0; i < dimensions.length; i++) {
|
var dimInfo = dimensionInfoMap[dimensions[i]];
|
var DataCtor = dataCtors[dimInfo.type];
|
storage[dimensions[i]] = new DataCtor(size);
|
}
|
|
var self = this;
|
if (!dimValueGetter) {
|
self.hasItemOption = false;
|
}
|
// Default dim value getter
|
dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
|
var value = modelUtil.getDataItemValue(dataItem);
|
// If any dataItem is like { value: 10 }
|
if (modelUtil.isDataItemOption(dataItem)) {
|
self.hasItemOption = true;
|
}
|
return modelUtil.converDataValue(
|
(value instanceof Array)
|
? value[dimIndex]
|
// If value is a single number or something else not array.
|
: value,
|
dimensionInfoMap[dimName]
|
);
|
};
|
|
for (var idx = 0; idx < data.length; idx++) {
|
var dataItem = data[idx];
|
// Each data item is value
|
// [1, 2]
|
// 2
|
// Bar chart, line chart which uses category axis
|
// only gives the 'y' value. 'x' value is the indices of cateogry
|
// Use a tempValue to normalize the value to be a (x, y) value
|
|
// Store the data by dimensions
|
for (var k = 0; k < dimensions.length; k++) {
|
var dim = dimensions[k];
|
var dimStorage = storage[dim];
|
// PENDING NULL is empty or zero
|
dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k);
|
}
|
|
indices.push(idx);
|
}
|
|
// Use the name in option and create id
|
for (var i = 0; i < data.length; i++) {
|
if (!nameList[i]) {
|
if (data[i] && data[i].name != null) {
|
nameList[i] = data[i].name;
|
}
|
}
|
var name = nameList[i] || '';
|
// Try using the id in option
|
var id = data[i] && data[i].id;
|
|
if (!id && name) {
|
// Use name as id and add counter to avoid same name
|
nameRepeatCount[name] = nameRepeatCount[name] || 0;
|
id = name;
|
if (nameRepeatCount[name] > 0) {
|
id += '__ec__' + nameRepeatCount[name];
|
}
|
nameRepeatCount[name]++;
|
}
|
id && (idList[i] = id);
|
}
|
|
this._nameList = nameList;
|
this._idList = idList;
|
};
|
|
/**
|
* @return {number}
|
*/
|
listProto.count = function () {
|
return this.indices.length;
|
};
|
|
/**
|
* Get value. Return NaN if idx is out of range.
|
* @param {string} dim Dim must be concrete name.
|
* @param {number} idx
|
* @param {boolean} stack
|
* @return {number}
|
*/
|
listProto.get = function (dim, idx, stack) {
|
var storage = this._storage;
|
var dataIndex = this.indices[idx];
|
|
// If value not exists
|
if (dataIndex == null) {
|
return NaN;
|
}
|
|
var value = storage[dim] && storage[dim][dataIndex];
|
// FIXME ordinal data type is not stackable
|
if (stack) {
|
var dimensionInfo = this._dimensionInfos[dim];
|
if (dimensionInfo && dimensionInfo.stackable) {
|
var stackedOn = this.stackedOn;
|
while (stackedOn) {
|
// Get no stacked data of stacked on
|
var stackedValue = stackedOn.get(dim, idx);
|
// Considering positive stack, negative stack and empty data
|
if ((value >= 0 && stackedValue > 0) // Positive stack
|
|| (value <= 0 && stackedValue < 0) // Negative stack
|
) {
|
value += stackedValue;
|
}
|
stackedOn = stackedOn.stackedOn;
|
}
|
}
|
}
|
return value;
|
};
|
|
/**
|
* Get value for multi dimensions.
|
* @param {Array.<string>} [dimensions] If ignored, using all dimensions.
|
* @param {number} idx
|
* @param {boolean} stack
|
* @return {number}
|
*/
|
listProto.getValues = function (dimensions, idx, stack) {
|
var values = [];
|
|
if (!zrUtil.isArray(dimensions)) {
|
stack = idx;
|
idx = dimensions;
|
dimensions = this.dimensions;
|
}
|
|
for (var i = 0, len = dimensions.length; i < len; i++) {
|
values.push(this.get(dimensions[i], idx, stack));
|
}
|
|
return values;
|
};
|
|
/**
|
* If value is NaN. Inlcuding '-'
|
* @param {string} dim
|
* @param {number} idx
|
* @return {number}
|
*/
|
listProto.hasValue = function (idx) {
|
var dimensions = this.dimensions;
|
var dimensionInfos = this._dimensionInfos;
|
for (var i = 0, len = dimensions.length; i < len; i++) {
|
if (
|
// Ordinal type can be string or number
|
dimensionInfos[dimensions[i]].type !== 'ordinal'
|
&& isNaN(this.get(dimensions[i], idx))
|
) {
|
return false;
|
}
|
}
|
return true;
|
};
|
|
/**
|
* Get extent of data in one dimension
|
* @param {string} dim
|
* @param {boolean} stack
|
* @param {Function} filter
|
*/
|
listProto.getDataExtent = function (dim, stack, filter) {
|
dim = this.getDimension(dim);
|
var dimData = this._storage[dim];
|
var dimInfo = this.getDimensionInfo(dim);
|
stack = (dimInfo && dimInfo.stackable) && stack;
|
var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)];
|
var value;
|
if (dimExtent) {
|
return dimExtent;
|
}
|
// var dimInfo = this._dimensionInfos[dim];
|
if (dimData) {
|
var min = Infinity;
|
var max = -Infinity;
|
// var isOrdinal = dimInfo.type === 'ordinal';
|
for (var i = 0, len = this.count(); i < len; i++) {
|
value = this.get(dim, i, stack);
|
// FIXME
|
// if (isOrdinal && typeof value === 'string') {
|
// value = zrUtil.indexOf(dimData, value);
|
// }
|
if (!filter || filter(value, dim, i)) {
|
value < min && (min = value);
|
value > max && (max = value);
|
}
|
}
|
return (this._extent[dim + !!stack] = [min, max]);
|
}
|
else {
|
return [Infinity, -Infinity];
|
}
|
};
|
|
/**
|
* Get sum of data in one dimension
|
* @param {string} dim
|
* @param {boolean} stack
|
*/
|
listProto.getSum = function (dim, stack) {
|
var dimData = this._storage[dim];
|
var sum = 0;
|
if (dimData) {
|
for (var i = 0, len = this.count(); i < len; i++) {
|
var value = this.get(dim, i, stack);
|
if (!isNaN(value)) {
|
sum += value;
|
}
|
}
|
}
|
return sum;
|
};
|
|
/**
|
* Retreive the index with given value
|
* @param {number} idx
|
* @param {number} value
|
* @return {number}
|
*/
|
// FIXME Precision of float value
|
listProto.indexOf = function (dim, value) {
|
var storage = this._storage;
|
var dimData = storage[dim];
|
var indices = this.indices;
|
|
if (dimData) {
|
for (var i = 0, len = indices.length; i < len; i++) {
|
var rawIndex = indices[i];
|
if (dimData[rawIndex] === value) {
|
return i;
|
}
|
}
|
}
|
return -1;
|
};
|
|
/**
|
* Retreive the index with given name
|
* @param {number} idx
|
* @param {number} name
|
* @return {number}
|
*/
|
listProto.indexOfName = function (name) {
|
var indices = this.indices;
|
var nameList = this._nameList;
|
|
for (var i = 0, len = indices.length; i < len; i++) {
|
var rawIndex = indices[i];
|
if (nameList[rawIndex] === name) {
|
return i;
|
}
|
}
|
|
return -1;
|
};
|
|
/**
|
* Retreive the index with given raw data index
|
* @param {number} idx
|
* @param {number} name
|
* @return {number}
|
*/
|
listProto.indexOfRawIndex = function (rawIndex) {
|
// Indices are ascending
|
var indices = this.indices;
|
|
// If rawIndex === dataIndex
|
var rawDataIndex = indices[rawIndex];
|
if (rawDataIndex != null && rawDataIndex === rawIndex) {
|
return rawIndex;
|
}
|
|
var left = 0;
|
var right = indices.length - 1;
|
while (left <= right) {
|
var mid = (left + right) / 2 | 0;
|
if (indices[mid] < rawIndex) {
|
left = mid + 1;
|
}
|
else if (indices[mid] > rawIndex) {
|
right = mid - 1;
|
}
|
else {
|
return mid;
|
}
|
}
|
return -1;
|
};
|
|
/**
|
* Retreive the index of nearest value
|
* @param {string} dim
|
* @param {number} value
|
* @param {boolean} stack If given value is after stacked
|
* @param {number} [maxDistance=Infinity]
|
* @return {Array.<number>} Considere multiple points has the same value.
|
*/
|
listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
|
var storage = this._storage;
|
var dimData = storage[dim];
|
var nearestIndices = [];
|
|
if (!dimData) {
|
return nearestIndices;
|
}
|
|
if (maxDistance == null) {
|
maxDistance = Infinity;
|
}
|
|
var minDist = Number.MAX_VALUE;
|
var minDiff = -1;
|
for (var i = 0, len = this.count(); i < len; i++) {
|
var diff = value - this.get(dim, i, stack);
|
var dist = Math.abs(diff);
|
if (diff <= maxDistance && dist <= minDist) {
|
// For the case of two data are same on xAxis, which has sequence data.
|
// Show the nearest index
|
// https://github.com/ecomfe/echarts/issues/2869
|
if (dist < minDist || (diff >= 0 && minDiff < 0)) {
|
minDist = dist;
|
minDiff = diff;
|
nearestIndices.length = 0;
|
}
|
nearestIndices.push(i);
|
}
|
}
|
return nearestIndices;
|
};
|
|
/**
|
* Get raw data index
|
* @param {number} idx
|
* @return {number}
|
*/
|
listProto.getRawIndex = function (idx) {
|
var rawIdx = this.indices[idx];
|
return rawIdx == null ? -1 : rawIdx;
|
};
|
|
/**
|
* Get raw data item
|
* @param {number} idx
|
* @return {number}
|
*/
|
listProto.getRawDataItem = function (idx) {
|
return this._rawData[this.getRawIndex(idx)];
|
};
|
|
/**
|
* @param {number} idx
|
* @param {boolean} [notDefaultIdx=false]
|
* @return {string}
|
*/
|
listProto.getName = function (idx) {
|
return this._nameList[this.indices[idx]] || '';
|
};
|
|
/**
|
* @param {number} idx
|
* @param {boolean} [notDefaultIdx=false]
|
* @return {string}
|
*/
|
listProto.getId = function (idx) {
|
return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + '');
|
};
|
|
|
function normalizeDimensions(dimensions) {
|
if (!zrUtil.isArray(dimensions)) {
|
dimensions = [dimensions];
|
}
|
return dimensions;
|
}
|
|
/**
|
* Data iteration
|
* @param {string|Array.<string>}
|
* @param {Function} cb
|
* @param {boolean} [stack=false]
|
* @param {*} [context=this]
|
*
|
* @example
|
* list.each('x', function (x, idx) {});
|
* list.each(['x', 'y'], function (x, y, idx) {});
|
* list.each(function (idx) {})
|
*/
|
listProto.each = function (dims, cb, stack, context) {
|
if (typeof dims === 'function') {
|
context = stack;
|
stack = cb;
|
cb = dims;
|
dims = [];
|
}
|
|
dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
|
|
var value = [];
|
var dimSize = dims.length;
|
var indices = this.indices;
|
|
context = context || this;
|
|
for (var i = 0; i < indices.length; i++) {
|
// Simple optimization
|
switch (dimSize) {
|
case 0:
|
cb.call(context, i);
|
break;
|
case 1:
|
cb.call(context, this.get(dims[0], i, stack), i);
|
break;
|
case 2:
|
cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
|
break;
|
default:
|
for (var k = 0; k < dimSize; k++) {
|
value[k] = this.get(dims[k], i, stack);
|
}
|
// Index
|
value[k] = i;
|
cb.apply(context, value);
|
}
|
}
|
};
|
|
/**
|
* Data filter
|
* @param {string|Array.<string>}
|
* @param {Function} cb
|
* @param {boolean} [stack=false]
|
* @param {*} [context=this]
|
*/
|
listProto.filterSelf = function (dimensions, cb, stack, context) {
|
if (typeof dimensions === 'function') {
|
context = stack;
|
stack = cb;
|
cb = dimensions;
|
dimensions = [];
|
}
|
|
dimensions = zrUtil.map(
|
normalizeDimensions(dimensions), this.getDimension, this
|
);
|
|
var newIndices = [];
|
var value = [];
|
var dimSize = dimensions.length;
|
var indices = this.indices;
|
|
context = context || this;
|
|
for (var i = 0; i < indices.length; i++) {
|
var keep;
|
// Simple optimization
|
if (dimSize === 1) {
|
keep = cb.call(
|
context, this.get(dimensions[0], i, stack), i
|
);
|
}
|
else {
|
for (var k = 0; k < dimSize; k++) {
|
value[k] = this.get(dimensions[k], i, stack);
|
}
|
value[k] = i;
|
keep = cb.apply(context, value);
|
}
|
if (keep) {
|
newIndices.push(indices[i]);
|
}
|
}
|
|
this.indices = newIndices;
|
|
// Reset data extent
|
this._extent = {};
|
|
return this;
|
};
|
|
/**
|
* Data mapping to a plain array
|
* @param {string|Array.<string>} [dimensions]
|
* @param {Function} cb
|
* @param {boolean} [stack=false]
|
* @param {*} [context=this]
|
* @return {Array}
|
*/
|
listProto.mapArray = function (dimensions, cb, stack, context) {
|
if (typeof dimensions === 'function') {
|
context = stack;
|
stack = cb;
|
cb = dimensions;
|
dimensions = [];
|
}
|
|
var result = [];
|
this.each(dimensions, function () {
|
result.push(cb && cb.apply(this, arguments));
|
}, stack, context);
|
return result;
|
};
|
|
function cloneListForMapAndSample(original, excludeDimensions) {
|
var allDimensions = original.dimensions;
|
var list = new List(
|
zrUtil.map(allDimensions, original.getDimensionInfo, original),
|
original.hostModel
|
);
|
// FIXME If needs stackedOn, value may already been stacked
|
transferProperties(list, original);
|
|
var storage = list._storage = {};
|
var originalStorage = original._storage;
|
// Init storage
|
for (var i = 0; i < allDimensions.length; i++) {
|
var dim = allDimensions[i];
|
var dimStore = originalStorage[dim];
|
if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
|
storage[dim] = new dimStore.constructor(
|
originalStorage[dim].length
|
);
|
}
|
else {
|
// Direct reference for other dimensions
|
storage[dim] = originalStorage[dim];
|
}
|
}
|
return list;
|
}
|
|
/**
|
* Data mapping to a new List with given dimensions
|
* @param {string|Array.<string>} dimensions
|
* @param {Function} cb
|
* @param {boolean} [stack=false]
|
* @param {*} [context=this]
|
* @return {Array}
|
*/
|
listProto.map = function (dimensions, cb, stack, context) {
|
dimensions = zrUtil.map(
|
normalizeDimensions(dimensions), this.getDimension, this
|
);
|
|
var list = cloneListForMapAndSample(this, dimensions);
|
// Following properties are all immutable.
|
// So we can reference to the same value
|
var indices = list.indices = this.indices;
|
|
var storage = list._storage;
|
|
var tmpRetValue = [];
|
this.each(dimensions, function () {
|
var idx = arguments[arguments.length - 1];
|
var retValue = cb && cb.apply(this, arguments);
|
if (retValue != null) {
|
// a number
|
if (typeof retValue === 'number') {
|
tmpRetValue[0] = retValue;
|
retValue = tmpRetValue;
|
}
|
for (var i = 0; i < retValue.length; i++) {
|
var dim = dimensions[i];
|
var dimStore = storage[dim];
|
var rawIdx = indices[idx];
|
if (dimStore) {
|
dimStore[rawIdx] = retValue[i];
|
}
|
}
|
}
|
}, stack, context);
|
|
return list;
|
};
|
|
/**
|
* Large data down sampling on given dimension
|
* @param {string} dimension
|
* @param {number} rate
|
* @param {Function} sampleValue
|
* @param {Function} sampleIndex Sample index for name and id
|
*/
|
listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
|
var list = cloneListForMapAndSample(this, [dimension]);
|
var storage = this._storage;
|
var targetStorage = list._storage;
|
|
var originalIndices = this.indices;
|
var indices = list.indices = [];
|
|
var frameValues = [];
|
var frameIndices = [];
|
var frameSize = Math.floor(1 / rate);
|
|
var dimStore = targetStorage[dimension];
|
var len = this.count();
|
// Copy data from original data
|
for (var i = 0; i < storage[dimension].length; i++) {
|
targetStorage[dimension][i] = storage[dimension][i];
|
}
|
for (var i = 0; i < len; i += frameSize) {
|
// Last frame
|
if (frameSize > len - i) {
|
frameSize = len - i;
|
frameValues.length = frameSize;
|
}
|
for (var k = 0; k < frameSize; k++) {
|
var idx = originalIndices[i + k];
|
frameValues[k] = dimStore[idx];
|
frameIndices[k] = idx;
|
}
|
var value = sampleValue(frameValues);
|
var idx = frameIndices[sampleIndex(frameValues, value) || 0];
|
// Only write value on the filtered data
|
dimStore[idx] = value;
|
indices.push(idx);
|
}
|
|
return list;
|
};
|
|
/**
|
* Get model of one data item.
|
*
|
* @param {number} idx
|
*/
|
// FIXME Model proxy ?
|
listProto.getItemModel = function (idx) {
|
var hostModel = this.hostModel;
|
idx = this.indices[idx];
|
return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel);
|
};
|
|
/**
|
* Create a data differ
|
* @param {module:echarts/data/List} otherList
|
* @return {module:echarts/data/DataDiffer}
|
*/
|
listProto.diff = function (otherList) {
|
var idList = this._idList;
|
var otherIdList = otherList && otherList._idList;
|
var val;
|
// Use prefix to avoid index to be the same as otherIdList[idx],
|
// which will cause weird udpate animation.
|
var prefix = 'e\0\0';
|
|
return new DataDiffer(
|
otherList ? otherList.indices : [],
|
this.indices,
|
function (idx) {
|
return (val = otherIdList[idx]) != null ? val : prefix + idx;
|
},
|
function (idx) {
|
return (val = idList[idx]) != null ? val : prefix + idx;
|
}
|
);
|
};
|
/**
|
* Get visual property.
|
* @param {string} key
|
*/
|
listProto.getVisual = function (key) {
|
var visual = this._visual;
|
return visual && visual[key];
|
};
|
|
/**
|
* Set visual property
|
* @param {string|Object} key
|
* @param {*} [value]
|
*
|
* @example
|
* setVisual('color', color);
|
* setVisual({
|
* 'color': color
|
* });
|
*/
|
listProto.setVisual = function (key, val) {
|
if (isObject(key)) {
|
for (var name in key) {
|
if (key.hasOwnProperty(name)) {
|
this.setVisual(name, key[name]);
|
}
|
}
|
return;
|
}
|
this._visual = this._visual || {};
|
this._visual[key] = val;
|
};
|
|
/**
|
* Set layout property.
|
* @param {string} key
|
* @param {*} [val]
|
*/
|
listProto.setLayout = function (key, val) {
|
if (isObject(key)) {
|
for (var name in key) {
|
if (key.hasOwnProperty(name)) {
|
this.setLayout(name, key[name]);
|
}
|
}
|
return;
|
}
|
this._layout[key] = val;
|
};
|
|
/**
|
* Get layout property.
|
* @param {string} key.
|
* @return {*}
|
*/
|
listProto.getLayout = function (key) {
|
return this._layout[key];
|
};
|
|
/**
|
* Get layout of single data item
|
* @param {number} idx
|
*/
|
listProto.getItemLayout = function (idx) {
|
return this._itemLayouts[idx];
|
};
|
|
/**
|
* Set layout of single data item
|
* @param {number} idx
|
* @param {Object} layout
|
* @param {boolean=} [merge=false]
|
*/
|
listProto.setItemLayout = function (idx, layout, merge) {
|
this._itemLayouts[idx] = merge
|
? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
|
: layout;
|
};
|
|
/**
|
* Clear all layout of single data item
|
*/
|
listProto.clearItemLayouts = function () {
|
this._itemLayouts.length = 0;
|
};
|
|
/**
|
* Get visual property of single data item
|
* @param {number} idx
|
* @param {string} key
|
* @param {boolean} ignoreParent
|
*/
|
listProto.getItemVisual = function (idx, key, ignoreParent) {
|
var itemVisual = this._itemVisuals[idx];
|
var val = itemVisual && itemVisual[key];
|
if (val == null && !ignoreParent) {
|
// Use global visual property
|
return this.getVisual(key);
|
}
|
return val;
|
};
|
|
/**
|
* Set visual property of single data item
|
*
|
* @param {number} idx
|
* @param {string|Object} key
|
* @param {*} [value]
|
*
|
* @example
|
* setItemVisual(0, 'color', color);
|
* setItemVisual(0, {
|
* 'color': color
|
* });
|
*/
|
listProto.setItemVisual = function (idx, key, value) {
|
var itemVisual = this._itemVisuals[idx] || {};
|
this._itemVisuals[idx] = itemVisual;
|
|
if (isObject(key)) {
|
for (var name in key) {
|
if (key.hasOwnProperty(name)) {
|
itemVisual[name] = key[name];
|
}
|
}
|
return;
|
}
|
itemVisual[key] = value;
|
};
|
|
/**
|
* Clear itemVisuals and list visual.
|
*/
|
listProto.clearAllVisual = function () {
|
this._visual = {};
|
this._itemVisuals = [];
|
};
|
|
var setItemDataAndSeriesIndex = function (child) {
|
child.seriesIndex = this.seriesIndex;
|
child.dataIndex = this.dataIndex;
|
child.dataType = this.dataType;
|
};
|
/**
|
* Set graphic element relative to data. It can be set as null
|
* @param {number} idx
|
* @param {module:zrender/Element} [el]
|
*/
|
listProto.setItemGraphicEl = function (idx, el) {
|
var hostModel = this.hostModel;
|
|
if (el) {
|
// Add data index and series index for indexing the data by element
|
// Useful in tooltip
|
el.dataIndex = idx;
|
el.dataType = this.dataType;
|
el.seriesIndex = hostModel && hostModel.seriesIndex;
|
if (el.type === 'group') {
|
el.traverse(setItemDataAndSeriesIndex, el);
|
}
|
}
|
|
this._graphicEls[idx] = el;
|
};
|
|
/**
|
* @param {number} idx
|
* @return {module:zrender/Element}
|
*/
|
listProto.getItemGraphicEl = function (idx) {
|
return this._graphicEls[idx];
|
};
|
|
/**
|
* @param {Function} cb
|
* @param {*} context
|
*/
|
listProto.eachItemGraphicEl = function (cb, context) {
|
zrUtil.each(this._graphicEls, function (el, idx) {
|
if (el) {
|
cb && cb.call(context, el, idx);
|
}
|
});
|
};
|
|
/**
|
* Shallow clone a new list except visual and layout properties, and graph elements.
|
* New list only change the indices.
|
*/
|
listProto.cloneShallow = function () {
|
var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
|
var list = new List(dimensionInfoList, this.hostModel);
|
|
// FIXME
|
list._storage = this._storage;
|
|
transferProperties(list, this);
|
|
|
// Clone will not change the data extent and indices
|
list.indices = this.indices.slice();
|
|
if (this._extent) {
|
list._extent = zrUtil.extend({}, this._extent);
|
}
|
|
return list;
|
};
|
|
/**
|
* Wrap some method to add more feature
|
* @param {string} methodName
|
* @param {Function} injectFunction
|
*/
|
listProto.wrapMethod = function (methodName, injectFunction) {
|
var originalMethod = this[methodName];
|
if (typeof originalMethod !== 'function') {
|
return;
|
}
|
this.__wrappedMethods = this.__wrappedMethods || [];
|
this.__wrappedMethods.push(methodName);
|
this[methodName] = function () {
|
var res = originalMethod.apply(this, arguments);
|
return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
|
};
|
};
|
|
// Methods that create a new list based on this list should be listed here.
|
// Notice that those method should `RETURN` the new list.
|
listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
|
// Methods that change indices of this list should be listed here.
|
listProto.CHANGABLE_METHODS = ['filterSelf'];
|
|
module.exports = List;
|
|
/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
|
|
/***/ },
|
/* 99 */
|
/***/ function(module, exports) {
|
|
'use strict';
|
|
|
function defaultKeyGetter(item) {
|
return item;
|
}
|
|
function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter) {
|
this._old = oldArr;
|
this._new = newArr;
|
|
this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
|
this._newKeyGetter = newKeyGetter || defaultKeyGetter;
|
}
|
|
DataDiffer.prototype = {
|
|
constructor: DataDiffer,
|
|
/**
|
* Callback function when add a data
|
*/
|
add: function (func) {
|
this._add = func;
|
return this;
|
},
|
|
/**
|
* Callback function when update a data
|
*/
|
update: function (func) {
|
this._update = func;
|
return this;
|
},
|
|
/**
|
* Callback function when remove a data
|
*/
|
remove: function (func) {
|
this._remove = func;
|
return this;
|
},
|
|
execute: function () {
|
var oldArr = this._old;
|
var newArr = this._new;
|
var oldKeyGetter = this._oldKeyGetter;
|
var newKeyGetter = this._newKeyGetter;
|
|
var oldDataIndexMap = {};
|
var newDataIndexMap = {};
|
var oldDataKeyArr = [];
|
var newDataKeyArr = [];
|
var i;
|
|
initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, oldKeyGetter);
|
initIndexMap(newArr, newDataIndexMap, newDataKeyArr, newKeyGetter);
|
|
// Travel by inverted order to make sure order consistency
|
// when duplicate keys exists (consider newDataIndex.pop() below).
|
// For performance consideration, these code below do not look neat.
|
for (i = 0; i < oldArr.length; i++) {
|
var key = oldDataKeyArr[i];
|
var idx = newDataIndexMap[key];
|
|
// idx can never be empty array here. see 'set null' logic below.
|
if (idx != null) {
|
// Consider there is duplicate key (for example, use dataItem.name as key).
|
// We should make sure every item in newArr and oldArr can be visited.
|
var len = idx.length;
|
if (len) {
|
len === 1 && (newDataIndexMap[key] = null);
|
idx = idx.unshift();
|
}
|
else {
|
newDataIndexMap[key] = null;
|
}
|
this._update && this._update(idx, i);
|
}
|
else {
|
this._remove && this._remove(i);
|
}
|
}
|
|
for (var i = 0; i < newDataKeyArr.length; i++) {
|
var key = newDataKeyArr[i];
|
if (newDataIndexMap.hasOwnProperty(key)) {
|
var idx = newDataIndexMap[key];
|
if (idx == null) {
|
continue;
|
}
|
// idx can never be empty array here. see 'set null' logic above.
|
if (!idx.length) {
|
this._add && this._add(idx);
|
}
|
else {
|
for (var j = 0, len = idx.length; j < len; j++) {
|
this._add && this._add(idx[j]);
|
}
|
}
|
}
|
}
|
}
|
};
|
|
function initIndexMap(arr, map, keyArr, keyGetter) {
|
for (var i = 0; i < arr.length; i++) {
|
var key = keyGetter(arr[i], i);
|
var existence = map[key];
|
if (existence == null) {
|
keyArr.push(key);
|
map[key] = i;
|
}
|
else {
|
if (!existence.length) {
|
map[key] = existence = [existence];
|
}
|
existence.push(i);
|
}
|
}
|
}
|
|
module.exports = DataDiffer;
|
|
|
/***/ },
|
/* 100 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var numberUtil = __webpack_require__(7);
|
var linearMap = numberUtil.linearMap;
|
var zrUtil = __webpack_require__(4);
|
|
function fixExtentWithBands(extent, nTick) {
|
var size = extent[1] - extent[0];
|
var len = nTick;
|
var margin = size / len / 2;
|
extent[0] += margin;
|
extent[1] -= margin;
|
}
|
|
var normalizedExtent = [0, 1];
|
/**
|
* @name module:echarts/coord/CartesianAxis
|
* @constructor
|
*/
|
var Axis = function (dim, scale, extent) {
|
|
/**
|
* Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius'
|
* @type {string}
|
*/
|
this.dim = dim;
|
|
/**
|
* Axis scale
|
* @type {module:echarts/coord/scale/*}
|
*/
|
this.scale = scale;
|
|
/**
|
* @type {Array.<number>}
|
* @private
|
*/
|
this._extent = extent || [0, 0];
|
|
/**
|
* @type {boolean}
|
*/
|
this.inverse = false;
|
|
/**
|
* Usually true when axis has a ordinal scale
|
* @type {boolean}
|
*/
|
this.onBand = false;
|
};
|
|
Axis.prototype = {
|
|
constructor: Axis,
|
|
/**
|
* If axis extent contain given coord
|
* @param {number} coord
|
* @return {boolean}
|
*/
|
contain: function (coord) {
|
var extent = this._extent;
|
var min = Math.min(extent[0], extent[1]);
|
var max = Math.max(extent[0], extent[1]);
|
return coord >= min && coord <= max;
|
},
|
|
/**
|
* If axis extent contain given data
|
* @param {number} data
|
* @return {boolean}
|
*/
|
containData: function (data) {
|
return this.contain(this.dataToCoord(data));
|
},
|
|
/**
|
* Get coord extent.
|
* @return {Array.<number>}
|
*/
|
getExtent: function () {
|
var ret = this._extent.slice();
|
return ret;
|
},
|
|
/**
|
* Get precision used for formatting
|
* @param {Array.<number>} [dataExtent]
|
* @return {number}
|
*/
|
getPixelPrecision: function (dataExtent) {
|
return numberUtil.getPixelPrecision(
|
dataExtent || this.scale.getExtent(),
|
this._extent
|
);
|
},
|
|
/**
|
* Set coord extent
|
* @param {number} start
|
* @param {number} end
|
*/
|
setExtent: function (start, end) {
|
var extent = this._extent;
|
extent[0] = start;
|
extent[1] = end;
|
},
|
|
/**
|
* Convert data to coord. Data is the rank if it has a ordinal scale
|
* @param {number} data
|
* @param {boolean} clamp
|
* @return {number}
|
*/
|
dataToCoord: function (data, clamp) {
|
var extent = this._extent;
|
var scale = this.scale;
|
data = scale.normalize(data);
|
|
if (this.onBand && scale.type === 'ordinal') {
|
extent = extent.slice();
|
fixExtentWithBands(extent, scale.count());
|
}
|
|
return linearMap(data, normalizedExtent, extent, clamp);
|
},
|
|
/**
|
* Convert coord to data. Data is the rank if it has a ordinal scale
|
* @param {number} coord
|
* @param {boolean} clamp
|
* @return {number}
|
*/
|
coordToData: function (coord, clamp) {
|
var extent = this._extent;
|
var scale = this.scale;
|
|
if (this.onBand && scale.type === 'ordinal') {
|
extent = extent.slice();
|
fixExtentWithBands(extent, scale.count());
|
}
|
|
var t = linearMap(coord, extent, normalizedExtent, clamp);
|
|
return this.scale.scale(t);
|
},
|
|
/**
|
* Convert pixel point to data in axis
|
* @param {Array.<number>} point
|
* @param {boolean} clamp
|
* @return {number} data
|
*/
|
pointToData: function (point, clamp) {
|
// Should be implemented in derived class if necessary.
|
},
|
|
/**
|
* @return {Array.<number>}
|
*/
|
getTicksCoords: function (alignWithLabel) {
|
if (this.onBand && !alignWithLabel) {
|
var bands = this.getBands();
|
var coords = [];
|
for (var i = 0; i < bands.length; i++) {
|
coords.push(bands[i][0]);
|
}
|
if (bands[i - 1]) {
|
coords.push(bands[i - 1][1]);
|
}
|
return coords;
|
}
|
else {
|
return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this);
|
}
|
},
|
|
/**
|
* Coords of labels are on the ticks or on the middle of bands
|
* @return {Array.<number>}
|
*/
|
getLabelsCoords: function () {
|
return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this);
|
},
|
|
/**
|
* Get bands.
|
*
|
* If axis has labels [1, 2, 3, 4]. Bands on the axis are
|
* |---1---|---2---|---3---|---4---|.
|
*
|
* @return {Array}
|
*/
|
// FIXME Situation when labels is on ticks
|
getBands: function () {
|
var extent = this.getExtent();
|
var bands = [];
|
var len = this.scale.count();
|
var start = extent[0];
|
var end = extent[1];
|
var span = end - start;
|
|
for (var i = 0; i < len; i++) {
|
bands.push([
|
span * i / len + start,
|
span * (i + 1) / len + start
|
]);
|
}
|
return bands;
|
},
|
|
/**
|
* Get width of band
|
* @return {number}
|
*/
|
getBandWidth: function () {
|
var axisExtent = this._extent;
|
var dataExtent = this.scale.getExtent();
|
|
var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0);
|
// Fix #2728, avoid NaN when only one data.
|
len === 0 && (len = 1);
|
|
var size = Math.abs(axisExtent[1] - axisExtent[0]);
|
|
return Math.abs(size) / len;
|
}
|
|
};
|
|
module.exports = Axis;
|
|
|
/***/ },
|
/* 101 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var createListFromArray = __webpack_require__(102);
|
var symbolUtil = __webpack_require__(104);
|
var axisHelper = __webpack_require__(105);
|
var axisModelCommonMixin = __webpack_require__(112);
|
var Model = __webpack_require__(12);
|
var util = __webpack_require__(4);
|
|
module.exports = {
|
/**
|
* Create a muti dimension List structure from seriesModel.
|
* @param {module:echarts/model/Model} seriesModel
|
* @return {module:echarts/data/List} list
|
*/
|
createList: function (seriesModel) {
|
var data = seriesModel.get('data');
|
return createListFromArray(data, seriesModel, seriesModel.ecModel);
|
},
|
|
/**
|
* Complete the dimensions array guessed from the data structure.
|
* @param {Array.<string>} dimensions Necessary dimensions, like ['x', 'y']
|
* @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]]
|
* @param {Object} [opt]
|
* @param {Array.<string>} [opt.defaultNames] Default names to fill not necessary dimensions, like ['value']
|
* @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.
|
* @param {number} [opt.dimCount] If not specified, guess by the first data item.
|
* @return {Array.<string>}
|
*/
|
completeDimensions: __webpack_require__(103),
|
|
/**
|
* Create a symbol element with given symbol configuration: shape, x, y, width, height, color
|
* @see http://echarts.baidu.com/option.html#series-scatter.symbol
|
* @param {string} symbolDesc
|
* @param {number} x
|
* @param {number} y
|
* @param {number} w
|
* @param {number} h
|
* @param {string} color
|
*/
|
createSymbol: symbolUtil.createSymbol,
|
|
/**
|
* Create scale
|
* @param {Array.<number>} dataExtent
|
* @param {Object|module:echarts/Model} option
|
*/
|
createScale: function (dataExtent, option) {
|
var axisModel = option;
|
if (!(option instanceof Model)) {
|
axisModel = new Model(option);
|
util.mixin(axisModel, axisModelCommonMixin);
|
}
|
|
var scale = axisHelper.createScaleByModel(axisModel);
|
scale.setExtent(dataExtent[0], dataExtent[1]);
|
|
axisHelper.niceScaleExtent(scale, axisModel);
|
return scale;
|
},
|
|
/**
|
* Mixin common methods to axis model,
|
*
|
* Inlcude methods
|
* `getFormattedLabels() => Array.<string>`
|
* `getCategories() => Array.<string>`
|
* `getMin(origin: boolean) => number`
|
* `getMax(origin: boolean) => number`
|
* `getNeedCrossZero() => boolean`
|
* `setRange(start: number, end: number)`
|
* `resetRange()`
|
*/
|
mixinAxisModelCommonMethods: function (Model) {
|
util.mixin(Model, axisModelCommonMixin);
|
}
|
};
|
|
|
/***/ },
|
/* 102 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var List = __webpack_require__(98);
|
var completeDimensions = __webpack_require__(103);
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var CoordinateSystem = __webpack_require__(26);
|
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 (true) {
|
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 axesInfo = creator && creator(data, seriesModel, ecModel);
|
var dimensions = axesInfo && axesInfo.dimensions;
|
if (!dimensions) {
|
// Get dimensions from registered coordinate system
|
dimensions = (registeredCoordSys && (
|
registeredCoordSys.getDimensionsInfo
|
? registeredCoordSys.getDimensionsInfo()
|
: registeredCoordSys.dimensions.slice()
|
)) || ['x', 'y'];
|
dimensions = completeDimensions(dimensions, data, {defaultNames: dimensions.concat(['value'])});
|
}
|
|
var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1;
|
|
var list = new List(dimensions, seriesModel);
|
|
var nameList = createNameList(axesInfo, data);
|
|
var categories = {};
|
var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data))
|
? function (itemOpt, dimName, dataIndex, dimIndex) {
|
// If any dataItem is like { value: 10 }
|
if (modelUtil.isDataItemOption(itemOpt)) {
|
list.hasItemOption = true;
|
}
|
|
// Use dataIndex as ordinal value in categoryAxis
|
return dimIndex === categoryIndex
|
? dataIndex
|
: converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]);
|
}
|
: function (itemOpt, dimName, dataIndex, dimIndex) {
|
var value = getDataItemValue(itemOpt);
|
var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]);
|
// If any dataItem is like { value: 10 }
|
if (modelUtil.isDataItemOption(itemOpt)) {
|
list.hasItemOption = true;
|
}
|
|
var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels;
|
if (categoryAxesModels && categoryAxesModels[dimName]) {
|
// If given value is a category string
|
if (typeof val === 'string') {
|
// Lazy get categories
|
categories[dimName] = categories[dimName]
|
|| categoryAxesModels[dimName].getCategories();
|
val = zrUtil.indexOf(categories[dimName], val);
|
if (val < 0 && !isNaN(val)) {
|
// In case some one write '1', '2' istead of 1, 2
|
val = +val;
|
}
|
}
|
}
|
return val;
|
};
|
|
list.hasItemOption = false;
|
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.
|
*/
|
var creators = {
|
|
cartesian2d: function (data, seriesModel, ecModel) {
|
|
var axesModels = zrUtil.map(['xAxis', 'yAxis'], function (name) {
|
return ecModel.queryComponents({
|
mainType: name,
|
index: seriesModel.get(name + 'Index'),
|
id: seriesModel.get(name + 'Id')
|
})[0];
|
});
|
var xAxisModel = axesModels[0];
|
var yAxisModel = axesModels[1];
|
|
if (true) {
|
if (!xAxisModel) {
|
throw new Error('xAxis "' + zrUtil.retrieve(
|
seriesModel.get('xAxisIndex'),
|
seriesModel.get('xAxisId'),
|
0
|
) + '" not found');
|
}
|
if (!yAxisModel) {
|
throw new Error('yAxis "' + zrUtil.retrieve(
|
seriesModel.get('xAxisIndex'),
|
seriesModel.get('yAxisId'),
|
0
|
) + '" 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';
|
var isYAxisCategory = yAxisType === 'category';
|
|
completeDimensions(dimensions, data, {defaultNames: ['x', 'y', 'z']});
|
|
var categoryAxesModels = {};
|
if (isXAxisCateogry) {
|
categoryAxesModels.x = xAxisModel;
|
}
|
if (isYAxisCategory) {
|
categoryAxesModels.y = yAxisModel;
|
}
|
return {
|
dimensions: dimensions,
|
categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1),
|
categoryAxesModels: categoryAxesModels
|
};
|
},
|
|
singleAxis: function (data, seriesModel, ecModel) {
|
|
var singleAxisModel = ecModel.queryComponents({
|
mainType: 'singleAxis',
|
index: seriesModel.get('singleAxisIndex'),
|
id: seriesModel.get('singleAxisId')
|
})[0];
|
|
if (true) {
|
if (!singleAxisModel) {
|
throw new Error('singleAxis should be specified.');
|
}
|
}
|
|
var singleAxisType = singleAxisModel.get('type');
|
var isCategory = singleAxisType === 'category';
|
|
var dimensions = [{
|
name: 'single',
|
type: getDimTypeByAxis(singleAxisType),
|
stackable: isStackable(singleAxisType)
|
}];
|
|
completeDimensions(dimensions, data);
|
|
var categoryAxesModels = {};
|
if (isCategory) {
|
categoryAxesModels.single = singleAxisModel;
|
}
|
|
return {
|
dimensions: dimensions,
|
categoryIndex: isCategory ? 0 : -1,
|
categoryAxesModels: categoryAxesModels
|
};
|
},
|
|
polar: function (data, seriesModel, ecModel) {
|
var polarModel = ecModel.queryComponents({
|
mainType: 'polar',
|
index: seriesModel.get('polarIndex'),
|
id: seriesModel.get('polarId')
|
})[0];
|
|
var angleAxisModel = polarModel.findAxisModel('angleAxis');
|
var radiusAxisModel = polarModel.findAxisModel('radiusAxis');
|
|
if (true) {
|
if (!angleAxisModel) {
|
throw new Error('angleAxis option not found');
|
}
|
if (!radiusAxisModel) {
|
throw new Error('radiusAxis 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';
|
var isRadiusAxisCateogry = radiusAxisType === 'category';
|
|
completeDimensions(dimensions, data, {defaultNames: ['radius', 'angle', 'value']});
|
|
var categoryAxesModels = {};
|
if (isRadiusAxisCateogry) {
|
categoryAxesModels.radius = radiusAxisModel;
|
}
|
if (isAngleAxisCateogry) {
|
categoryAxesModels.angle = angleAxisModel;
|
}
|
return {
|
dimensions: dimensions,
|
categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1),
|
categoryAxesModels: categoryAxesModels
|
};
|
},
|
|
geo: function (data, seriesModel, ecModel) {
|
// TODO Region
|
// 多个散点图系列在同一个地区的时候
|
return {
|
dimensions: completeDimensions([
|
{name: 'lng'},
|
{name: 'lat'}
|
], data, {defaultNames: ['lng', 'lat', 'value']})
|
};
|
}
|
};
|
|
function createNameList(result, data) {
|
var nameList = [];
|
|
var categoryDim = result && result.dimensions[result.categoryIndex];
|
var categoryAxisModel;
|
if (categoryDim) {
|
categoryAxisModel = result.categoryAxesModels[categoryDim.name];
|
}
|
|
if (categoryAxisModel) {
|
// FIXME Two category axis
|
var categories = 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;
|
}
|
|
module.exports = createListFromArray;
|
|
|
|
/***/ },
|
/* 103 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Complete dimensions by data (guess dimension).
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
|
/**
|
* Complete the dimensions array guessed from the data structure.
|
* @param {Array.<string>} dimensions Necessary dimensions, like ['x', 'y']
|
* @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]]
|
* @param {Object} [opt]
|
* @param {Array.<string>} [opt.defaultNames] Default names to fill not necessary dimensions, like ['value']
|
* @param {string} [opt.extraPrefix] Prefix of name when filling the left dimensions.
|
* @param {number} [opt.dimCount] If not specified, guess by the first data item.
|
* @return {Array.<string>}
|
*/
|
function completeDimensions(dimensions, data, opt) {
|
if (!data) {
|
return dimensions;
|
}
|
|
opt = opt || {};
|
|
var dimCount = opt.dimCount;
|
if (dimCount == null) {
|
var value0 = retrieveValue(data[0]);
|
dimCount = zrUtil.isArray(value0) && value0.length || 1;
|
}
|
|
var defaultNames = opt.defaultNames || [];
|
var extraPrefix = opt.extraPrefix || 'extra';
|
for (var i = 0; i < dimCount; i++) {
|
if (!dimensions[i]) {
|
var name = defaultNames[i] || (extraPrefix + (i - defaultNames.length));
|
dimensions[i] = guessOrdinal(data, i)
|
? {type: 'ordinal', name: name}
|
: name;
|
}
|
}
|
|
return dimensions;
|
}
|
|
// The rule should not be complex, otherwise user might not
|
// be able to known where the data is wrong.
|
var guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) {
|
for (var i = 0, len = data.length; i < len; i++) {
|
var value = retrieveValue(data[i]);
|
|
if (!zrUtil.isArray(value)) {
|
return false;
|
}
|
|
var value = value[dimIndex];
|
if (value != null && isFinite(value)) {
|
return false;
|
}
|
else if (zrUtil.isString(value) && value !== '-') {
|
return true;
|
}
|
}
|
return false;
|
};
|
|
function retrieveValue(o) {
|
return zrUtil.isArray(o) ? o : zrUtil.isObject(o) ? o.value: o;
|
}
|
|
module.exports = completeDimensions;
|
|
|
|
/***/ },
|
/* 104 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// Symbol factory
|
|
|
var graphic = __webpack_require__(44);
|
var BoundingRect = __webpack_require__(9);
|
|
/**
|
* Triangle shape
|
* @inner
|
*/
|
var Triangle = graphic.extendShape({
|
type: 'triangle',
|
shape: {
|
cx: 0,
|
cy: 0,
|
width: 0,
|
height: 0
|
},
|
buildPath: function (path, shape) {
|
var cx = shape.cx;
|
var cy = shape.cy;
|
var width = shape.width / 2;
|
var height = shape.height / 2;
|
path.moveTo(cx, cy - height);
|
path.lineTo(cx + width, cy + height);
|
path.lineTo(cx - width, cy + height);
|
path.closePath();
|
}
|
});
|
/**
|
* Diamond shape
|
* @inner
|
*/
|
var Diamond = graphic.extendShape({
|
type: 'diamond',
|
shape: {
|
cx: 0,
|
cy: 0,
|
width: 0,
|
height: 0
|
},
|
buildPath: function (path, shape) {
|
var cx = shape.cx;
|
var cy = shape.cy;
|
var width = shape.width / 2;
|
var height = shape.height / 2;
|
path.moveTo(cx, cy - height);
|
path.lineTo(cx + width, cy);
|
path.lineTo(cx, cy + height);
|
path.lineTo(cx - width, cy);
|
path.closePath();
|
}
|
});
|
|
/**
|
* Pin shape
|
* @inner
|
*/
|
var Pin = graphic.extendShape({
|
type: 'pin',
|
shape: {
|
// x, y on the cusp
|
x: 0,
|
y: 0,
|
width: 0,
|
height: 0
|
},
|
|
buildPath: function (path, shape) {
|
var x = shape.x;
|
var y = shape.y;
|
var w = shape.width / 5 * 3;
|
// Height must be larger than width
|
var h = Math.max(w, shape.height);
|
var r = w / 2;
|
|
// Dist on y with tangent point and circle center
|
var dy = r * r / (h - r);
|
var cy = y - h + r + dy;
|
var angle = Math.asin(dy / r);
|
// Dist on x with tangent point and circle center
|
var dx = Math.cos(angle) * r;
|
|
var tanX = Math.sin(angle);
|
var tanY = Math.cos(angle);
|
|
path.arc(
|
x, cy, r,
|
Math.PI - angle,
|
Math.PI * 2 + angle
|
);
|
|
var cpLen = r * 0.6;
|
var cpLen2 = r * 0.7;
|
path.bezierCurveTo(
|
x + dx - tanX * cpLen, cy + dy + tanY * cpLen,
|
x, y - cpLen2,
|
x, y
|
);
|
path.bezierCurveTo(
|
x, y - cpLen2,
|
x - dx + tanX * cpLen, cy + dy + tanY * cpLen,
|
x - dx, cy + dy
|
);
|
path.closePath();
|
}
|
});
|
|
/**
|
* Arrow shape
|
* @inner
|
*/
|
var Arrow = graphic.extendShape({
|
|
type: 'arrow',
|
|
shape: {
|
x: 0,
|
y: 0,
|
width: 0,
|
height: 0
|
},
|
|
buildPath: function (ctx, shape) {
|
var height = shape.height;
|
var width = shape.width;
|
var x = shape.x;
|
var y = shape.y;
|
var dx = width / 3 * 2;
|
ctx.moveTo(x, y);
|
ctx.lineTo(x + dx, y + height);
|
ctx.lineTo(x, y + height / 4 * 3);
|
ctx.lineTo(x - dx, y + height);
|
ctx.lineTo(x, y);
|
ctx.closePath();
|
}
|
});
|
|
/**
|
* Map of path contructors
|
* @type {Object.<string, module:zrender/graphic/Path>}
|
*/
|
var symbolCtors = {
|
line: graphic.Line,
|
|
rect: graphic.Rect,
|
|
roundRect: graphic.Rect,
|
|
square: graphic.Rect,
|
|
circle: graphic.Circle,
|
|
diamond: Diamond,
|
|
pin: Pin,
|
|
arrow: Arrow,
|
|
triangle: Triangle
|
};
|
|
var symbolShapeMakers = {
|
|
line: function (x, y, w, h, shape) {
|
// FIXME
|
shape.x1 = x;
|
shape.y1 = y + h / 2;
|
shape.x2 = x + w;
|
shape.y2 = y + h / 2;
|
},
|
|
rect: function (x, y, w, h, shape) {
|
shape.x = x;
|
shape.y = y;
|
shape.width = w;
|
shape.height = h;
|
},
|
|
roundRect: function (x, y, w, h, shape) {
|
shape.x = x;
|
shape.y = y;
|
shape.width = w;
|
shape.height = h;
|
shape.r = Math.min(w, h) / 4;
|
},
|
|
square: function (x, y, w, h, shape) {
|
var size = Math.min(w, h);
|
shape.x = x;
|
shape.y = y;
|
shape.width = size;
|
shape.height = size;
|
},
|
|
circle: function (x, y, w, h, shape) {
|
// Put circle in the center of square
|
shape.cx = x + w / 2;
|
shape.cy = y + h / 2;
|
shape.r = Math.min(w, h) / 2;
|
},
|
|
diamond: function (x, y, w, h, shape) {
|
shape.cx = x + w / 2;
|
shape.cy = y + h / 2;
|
shape.width = w;
|
shape.height = h;
|
},
|
|
pin: function (x, y, w, h, shape) {
|
shape.x = x + w / 2;
|
shape.y = y + h / 2;
|
shape.width = w;
|
shape.height = h;
|
},
|
|
arrow: function (x, y, w, h, shape) {
|
shape.x = x + w / 2;
|
shape.y = y + h / 2;
|
shape.width = w;
|
shape.height = h;
|
},
|
|
triangle: function (x, y, w, h, shape) {
|
shape.cx = x + w / 2;
|
shape.cy = y + h / 2;
|
shape.width = w;
|
shape.height = h;
|
}
|
};
|
|
var symbolBuildProxies = {};
|
for (var name in symbolCtors) {
|
if (symbolCtors.hasOwnProperty(name)) {
|
symbolBuildProxies[name] = new symbolCtors[name]();
|
}
|
}
|
|
var Symbol = graphic.extendShape({
|
|
type: 'symbol',
|
|
shape: {
|
symbolType: '',
|
x: 0,
|
y: 0,
|
width: 0,
|
height: 0
|
},
|
|
beforeBrush: function () {
|
var style = this.style;
|
var shape = this.shape;
|
// FIXME
|
if (shape.symbolType === 'pin' && style.textPosition === 'inside') {
|
style.textPosition = ['50%', '40%'];
|
style.textAlign = 'center';
|
style.textVerticalAlign = 'middle';
|
}
|
},
|
|
buildPath: function (ctx, shape, inBundle) {
|
var symbolType = shape.symbolType;
|
var proxySymbol = symbolBuildProxies[symbolType];
|
if (shape.symbolType !== 'none') {
|
if (!proxySymbol) {
|
// Default rect
|
symbolType = 'rect';
|
proxySymbol = symbolBuildProxies[symbolType];
|
}
|
symbolShapeMakers[symbolType](
|
shape.x, shape.y, shape.width, shape.height, proxySymbol.shape
|
);
|
proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle);
|
}
|
}
|
});
|
|
// Provide setColor helper method to avoid determine if set the fill or stroke outside
|
var symbolPathSetColor = function (color) {
|
if (this.type !== 'image') {
|
var symbolStyle = this.style;
|
var symbolShape = this.shape;
|
if (symbolShape && symbolShape.symbolType === 'line') {
|
symbolStyle.stroke = color;
|
}
|
else if (this.__isEmptyBrush) {
|
symbolStyle.stroke = color;
|
symbolStyle.fill = '#fff';
|
}
|
else {
|
// FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
|
symbolStyle.fill && (symbolStyle.fill = color);
|
symbolStyle.stroke && (symbolStyle.stroke = color);
|
}
|
this.dirty(false);
|
}
|
};
|
|
var symbolUtil = {
|
/**
|
* Create a symbol element with given symbol configuration: shape, x, y, width, height, color
|
* @param {string} symbolType
|
* @param {number} x
|
* @param {number} y
|
* @param {number} w
|
* @param {number} h
|
* @param {string} color
|
*/
|
createSymbol: function (symbolType, x, y, w, h, color) {
|
// TODO Support image object, DynamicImage.
|
|
var isEmpty = symbolType.indexOf('empty') === 0;
|
if (isEmpty) {
|
symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6);
|
}
|
var symbolPath;
|
|
if (symbolType.indexOf('image://') === 0) {
|
symbolPath = new graphic.Image({
|
style: {
|
image: symbolType.slice(8),
|
x: x,
|
y: y,
|
width: w,
|
height: h
|
}
|
});
|
}
|
else if (symbolType.indexOf('path://') === 0) {
|
symbolPath = graphic.makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h));
|
}
|
else {
|
symbolPath = new Symbol({
|
shape: {
|
symbolType: symbolType,
|
x: x,
|
y: y,
|
width: w,
|
height: h
|
}
|
});
|
}
|
|
symbolPath.__isEmptyBrush = isEmpty;
|
|
symbolPath.setColor = symbolPathSetColor;
|
|
symbolPath.setColor(color);
|
|
return symbolPath;
|
}
|
};
|
|
module.exports = symbolUtil;
|
|
|
/***/ },
|
/* 105 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var OrdinalScale = __webpack_require__(106);
|
var IntervalScale = __webpack_require__(108);
|
__webpack_require__(110);
|
__webpack_require__(111);
|
var Scale = __webpack_require__(107);
|
|
var numberUtil = __webpack_require__(7);
|
var zrUtil = __webpack_require__(4);
|
var textContain = __webpack_require__(8);
|
var axisHelper = {};
|
|
/**
|
* Get axis scale extent before niced.
|
* Item of returned array can only be number (including Infinity and NaN).
|
*/
|
axisHelper.getScaleExtent = function (scale, model) {
|
var scaleType = scale.type;
|
|
var min = model.getMin();
|
var max = model.getMax();
|
var fixMin = min != null;
|
var fixMax = max != null;
|
var originalExtent = scale.getExtent();
|
|
var axisDataLen;
|
var boundaryGap;
|
var span;
|
if (scaleType === 'ordinal') {
|
axisDataLen = (model.get('data') || []).length;
|
}
|
else {
|
boundaryGap = model.get('boundaryGap');
|
if (!zrUtil.isArray(boundaryGap)) {
|
boundaryGap = [boundaryGap || 0, boundaryGap || 0];
|
}
|
if (typeof boundaryGap[0] === 'boolean') {
|
if (true) {
|
console.warn('Boolean type for boundaryGap is only '
|
+ 'allowed for ordinal axis. Please use string in '
|
+ 'percentage instead, e.g., "20%". Currently, '
|
+ 'boundaryGap is set to be 0.');
|
}
|
boundaryGap = [0, 0];
|
}
|
boundaryGap[0] = numberUtil.parsePercent(boundaryGap[0], 1);
|
boundaryGap[1] = numberUtil.parsePercent(boundaryGap[1], 1);
|
span = (originalExtent[1] - originalExtent[0])
|
|| Math.abs(originalExtent[0]);
|
}
|
|
// Notice: When min/max is not set (that is, when there are null/undefined,
|
// which is the most common case), these cases should be ensured:
|
// (1) For 'ordinal', show all axis.data.
|
// (2) For others:
|
// + `boundaryGap` is applied (if min/max set, boundaryGap is
|
// disabled).
|
// + If `needCrossZero`, min/max should be zero, otherwise, min/max should
|
// be the result that originalExtent enlarged by boundaryGap.
|
// (3) If no data, it should be ensured that `scale.setBlank` is set.
|
|
// FIXME
|
// (1) When min/max is 'dataMin' or 'dataMax', should boundaryGap be able to used?
|
// (2) When `needCrossZero` and all data is positive/negative, should it be ensured
|
// that the results processed by boundaryGap are positive/negative?
|
|
if (min == null) {
|
min = scaleType === 'ordinal'
|
? (axisDataLen ? 0 : NaN)
|
: originalExtent[0] - boundaryGap[0] * span;
|
}
|
if (max == null) {
|
max = scaleType === 'ordinal'
|
? (axisDataLen ? axisDataLen - 1 : NaN)
|
: originalExtent[1] + boundaryGap[1] * span;
|
}
|
|
if (min === 'dataMin') {
|
min = originalExtent[0];
|
}
|
if (max === 'dataMax') {
|
max = originalExtent[1];
|
}
|
|
(min == null || !isFinite(min)) && (min = NaN);
|
(max == null || !isFinite(max)) && (max = NaN);
|
|
scale.setBlank(zrUtil.eqNaN(min) || zrUtil.eqNaN(max));
|
|
// Evaluate if axis needs cross zero
|
if (model.getNeedCrossZero()) {
|
// Axis is over zero and min is not set
|
if (min > 0 && max > 0 && !fixMin) {
|
min = 0;
|
}
|
// Axis is under zero and max is not set
|
if (min < 0 && max < 0 && !fixMax) {
|
max = 0;
|
}
|
}
|
|
return [min, max];
|
};
|
|
axisHelper.niceScaleExtent = function (scale, model) {
|
var extent = axisHelper.getScaleExtent(scale, model);
|
var fixMin = model.getMin() != null;
|
var fixMax = model.getMax() != null;
|
var splitNumber = model.get('splitNumber');
|
|
if (scale.type === 'log') {
|
scale.base = model.get('logBase');
|
}
|
|
scale.setExtent(extent[0], extent[1]);
|
scale.niceExtent(splitNumber, fixMin, fixMax);
|
|
// Use minInterval to constraint the calculated interval.
|
// If calculated interval is less than minInterval. increase the interval quantity until
|
// it is larger than minInterval.
|
// For example:
|
// minInterval is 1, calculated interval is 0.2, so increase it to be 1. In this way we can get
|
// an integer axis.
|
var minInterval = model.get('minInterval');
|
if (isFinite(minInterval) && !fixMin && !fixMax && scale.type === 'interval') {
|
var interval = scale.getInterval();
|
var intervalScale = Math.max(Math.abs(interval), minInterval) / interval;
|
// while (interval < minInterval) {
|
// var quantity = numberUtil.quantity(interval);
|
// interval = quantity * 10;
|
// scaleQuantity *= 10;
|
// }
|
extent = scale.getExtent();
|
var origin = (extent[1] + extent[0]) / 2;
|
scale.setExtent(
|
intervalScale * (extent[0] - origin) + origin,
|
intervalScale * (extent[1] - origin) + origin
|
);
|
scale.niceExtent(splitNumber);
|
}
|
|
// If some one specified the min, max. And the default calculated interval
|
// is not good enough. He can specify the interval. It is often appeared
|
// in angle axis with angle 0 - 360. Interval calculated in interval scale is hard
|
// to be 60.
|
// FIXME
|
var interval = model.get('interval');
|
if (interval != null) {
|
scale.setInterval && scale.setInterval(interval);
|
}
|
};
|
|
/**
|
* @param {module:echarts/model/Model} model
|
* @param {string} [axisType] Default retrieve from model.type
|
* @return {module:echarts/scale/*}
|
*/
|
axisHelper.createScaleByModel = function(model, axisType) {
|
axisType = axisType || model.get('type');
|
if (axisType) {
|
switch (axisType) {
|
// Buildin scale
|
case 'category':
|
return new OrdinalScale(
|
model.getCategories(), [Infinity, -Infinity]
|
);
|
case 'value':
|
return new IntervalScale();
|
// Extended scale, like time and log
|
default:
|
return (Scale.getClass(axisType) || IntervalScale).create(model);
|
}
|
}
|
};
|
|
/**
|
* Check if the axis corss 0
|
*/
|
axisHelper.ifAxisCrossZero = function (axis) {
|
var dataExtent = axis.scale.getExtent();
|
var min = dataExtent[0];
|
var max = dataExtent[1];
|
return !((min > 0 && max > 0) || (min < 0 && max < 0));
|
};
|
|
/**
|
* @param {Array.<number>} tickCoords In axis self coordinate.
|
* @param {Array.<string>} labels
|
* @param {string} font
|
* @param {boolean} isAxisHorizontal
|
* @return {number}
|
*/
|
axisHelper.getAxisLabelInterval = function (tickCoords, labels, font, isAxisHorizontal) {
|
// FIXME
|
// 不同角的axis和label,不只是horizontal和vertical.
|
|
var textSpaceTakenRect;
|
var autoLabelInterval = 0;
|
var accumulatedLabelInterval = 0;
|
|
var step = 1;
|
if (labels.length > 40) {
|
// Simple optimization for large amount of labels
|
step = Math.floor(labels.length / 40);
|
}
|
|
for (var i = 0; i < tickCoords.length; i += step) {
|
var tickCoord = tickCoords[i];
|
var rect = textContain.getBoundingRect(
|
labels[i], font, 'center', 'top'
|
);
|
rect[isAxisHorizontal ? 'x' : 'y'] += tickCoord;
|
// FIXME Magic number 1.5
|
rect[isAxisHorizontal ? 'width' : 'height'] *= 1.3;
|
if (!textSpaceTakenRect) {
|
textSpaceTakenRect = rect.clone();
|
}
|
// There is no space for current label;
|
else if (textSpaceTakenRect.intersect(rect)) {
|
accumulatedLabelInterval++;
|
autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval);
|
}
|
else {
|
textSpaceTakenRect.union(rect);
|
// Reset
|
accumulatedLabelInterval = 0;
|
}
|
}
|
if (autoLabelInterval === 0 && step > 1) {
|
return step;
|
}
|
return (autoLabelInterval + 1) * step - 1;
|
};
|
|
/**
|
* @param {Object} axis
|
* @param {Function} labelFormatter
|
* @return {Array.<string>}
|
*/
|
axisHelper.getFormattedLabels = function (axis, labelFormatter) {
|
var scale = axis.scale;
|
var labels = scale.getTicksLabels();
|
var ticks = scale.getTicks();
|
if (typeof labelFormatter === 'string') {
|
labelFormatter = (function (tpl) {
|
return function (val) {
|
return tpl.replace('{value}', val != null ? val : '');
|
};
|
})(labelFormatter);
|
// Consider empty array
|
return zrUtil.map(labels, labelFormatter);
|
}
|
else if (typeof labelFormatter === 'function') {
|
return zrUtil.map(ticks, function (tick, idx) {
|
return labelFormatter(
|
axisHelper.getAxisRawValue(axis, tick),
|
idx
|
);
|
}, this);
|
}
|
else {
|
return labels;
|
}
|
};
|
|
axisHelper.getAxisRawValue = function (axis, value) {
|
// In category axis with data zoom, tick is not the original
|
// index of axis.data. So tick should not be exposed to user
|
// in category axis.
|
return axis.type === 'category' ? axis.scale.getLabel(value) : value;
|
};
|
|
module.exports = axisHelper;
|
|
|
/***/ },
|
/* 106 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Linear continuous scale
|
* @module echarts/coord/scale/Ordinal
|
*
|
* http://en.wikipedia.org/wiki/Level_of_measurement
|
*/
|
|
// FIXME only one data
|
|
|
var zrUtil = __webpack_require__(4);
|
var Scale = __webpack_require__(107);
|
|
var scaleProto = Scale.prototype;
|
|
var OrdinalScale = Scale.extend({
|
|
type: 'ordinal',
|
|
init: function (data, extent) {
|
this._data = data;
|
this._extent = extent || [0, data.length - 1];
|
},
|
|
parse: function (val) {
|
return typeof val === 'string'
|
? zrUtil.indexOf(this._data, val)
|
// val might be float.
|
: Math.round(val);
|
},
|
|
contain: function (rank) {
|
rank = this.parse(rank);
|
return scaleProto.contain.call(this, rank)
|
&& this._data[rank] != null;
|
},
|
|
/**
|
* Normalize given rank or name to linear [0, 1]
|
* @param {number|string} [val]
|
* @return {number}
|
*/
|
normalize: function (val) {
|
return scaleProto.normalize.call(this, this.parse(val));
|
},
|
|
scale: function (val) {
|
return Math.round(scaleProto.scale.call(this, val));
|
},
|
|
/**
|
* @return {Array}
|
*/
|
getTicks: function () {
|
var ticks = [];
|
var extent = this._extent;
|
var rank = extent[0];
|
|
while (rank <= extent[1]) {
|
ticks.push(rank);
|
rank++;
|
}
|
|
return ticks;
|
},
|
|
/**
|
* Get item on rank n
|
* @param {number} n
|
* @return {string}
|
*/
|
getLabel: function (n) {
|
return this._data[n];
|
},
|
|
/**
|
* @return {number}
|
*/
|
count: function () {
|
return this._extent[1] - this._extent[0] + 1;
|
},
|
|
/**
|
* @override
|
*/
|
unionExtentFromData: function (data, dim) {
|
this.unionExtent(data.getDataExtent(dim, false));
|
},
|
|
niceTicks: zrUtil.noop,
|
niceExtent: zrUtil.noop
|
});
|
|
/**
|
* @return {module:echarts/scale/Time}
|
*/
|
OrdinalScale.create = function () {
|
return new OrdinalScale();
|
};
|
|
module.exports = OrdinalScale;
|
|
|
/***/ },
|
/* 107 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* // Scale class management
|
* @module echarts/scale/Scale
|
*/
|
|
|
var clazzUtil = __webpack_require__(13);
|
|
/**
|
* @param {Object} [setting]
|
*/
|
function Scale(setting) {
|
this._setting = setting || {};
|
|
/**
|
* Extent
|
* @type {Array.<number>}
|
* @protected
|
*/
|
this._extent = [Infinity, -Infinity];
|
|
/**
|
* Step is calculated in adjustExtent
|
* @type {Array.<number>}
|
* @protected
|
*/
|
this._interval = 0;
|
|
this.init && this.init.apply(this, arguments);
|
}
|
|
var scaleProto = Scale.prototype;
|
|
/**
|
* Parse input val to valid inner number.
|
* @param {*} val
|
* @return {number}
|
*/
|
scaleProto.parse = function (val) {
|
// Notice: This would be a trap here, If the implementation
|
// of this method depends on extent, and this method is used
|
// before extent set (like in dataZoom), it would be wrong.
|
// Nevertheless, parse does not depend on extent generally.
|
return val;
|
};
|
|
scaleProto.getSetting = function (name) {
|
return this._setting[name];
|
};
|
|
scaleProto.contain = function (val) {
|
var extent = this._extent;
|
return val >= extent[0] && val <= extent[1];
|
};
|
|
/**
|
* Normalize value to linear [0, 1], return 0.5 if extent span is 0
|
* @param {number} val
|
* @return {number}
|
*/
|
scaleProto.normalize = function (val) {
|
var extent = this._extent;
|
if (extent[1] === extent[0]) {
|
return 0.5;
|
}
|
return (val - extent[0]) / (extent[1] - extent[0]);
|
};
|
|
/**
|
* Scale normalized value
|
* @param {number} val
|
* @return {number}
|
*/
|
scaleProto.scale = function (val) {
|
var extent = this._extent;
|
return val * (extent[1] - extent[0]) + extent[0];
|
};
|
|
/**
|
* Set extent from data
|
* @param {Array.<number>} other
|
*/
|
scaleProto.unionExtent = function (other) {
|
var extent = this._extent;
|
other[0] < extent[0] && (extent[0] = other[0]);
|
other[1] > extent[1] && (extent[1] = other[1]);
|
// not setExtent because in log axis it may transformed to power
|
// this.setExtent(extent[0], extent[1]);
|
};
|
|
/**
|
* Set extent from data
|
* @param {module:echarts/data/List} data
|
* @param {string} dim
|
*/
|
scaleProto.unionExtentFromData = function (data, dim) {
|
this.unionExtent(data.getDataExtent(dim, true));
|
};
|
|
/**
|
* Get extent
|
* @return {Array.<number>}
|
*/
|
scaleProto.getExtent = function () {
|
return this._extent.slice();
|
};
|
|
/**
|
* Set extent
|
* @param {number} start
|
* @param {number} end
|
*/
|
scaleProto.setExtent = function (start, end) {
|
var thisExtent = this._extent;
|
if (!isNaN(start)) {
|
thisExtent[0] = start;
|
}
|
if (!isNaN(end)) {
|
thisExtent[1] = end;
|
}
|
};
|
|
/**
|
* @return {Array.<string>}
|
*/
|
scaleProto.getTicksLabels = function () {
|
var labels = [];
|
var ticks = this.getTicks();
|
for (var i = 0; i < ticks.length; i++) {
|
labels.push(this.getLabel(ticks[i]));
|
}
|
return labels;
|
};
|
|
/**
|
* When axis extent depends on data and no data exists,
|
* axis ticks should not be drawn, which is named 'blank'.
|
*/
|
scaleProto.isBlank = function () {
|
return this._isBlank;
|
},
|
|
/**
|
* When axis extent depends on data and no data exists,
|
* axis ticks should not be drawn, which is named 'blank'.
|
*/
|
scaleProto.setBlank = function (isBlank) {
|
this._isBlank = isBlank;
|
};
|
|
|
clazzUtil.enableClassExtend(Scale);
|
clazzUtil.enableClassManagement(Scale, {
|
registerWhenExtend: true
|
});
|
|
module.exports = Scale;
|
|
|
/***/ },
|
/* 108 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Interval scale
|
* @module echarts/scale/Interval
|
*/
|
|
|
|
var numberUtil = __webpack_require__(7);
|
var formatUtil = __webpack_require__(6);
|
var Scale = __webpack_require__(107);
|
var helper = __webpack_require__(109);
|
|
var roundNumber = numberUtil.round;
|
|
/**
|
* @alias module:echarts/coord/scale/Interval
|
* @constructor
|
*/
|
var IntervalScale = Scale.extend({
|
|
type: 'interval',
|
|
_interval: 0,
|
|
_intervalPrecision: 2,
|
|
setExtent: function (start, end) {
|
var thisExtent = this._extent;
|
//start,end may be a Number like '25',so...
|
if (!isNaN(start)) {
|
thisExtent[0] = parseFloat(start);
|
}
|
if (!isNaN(end)) {
|
thisExtent[1] = parseFloat(end);
|
}
|
},
|
|
unionExtent: function (other) {
|
var extent = this._extent;
|
other[0] < extent[0] && (extent[0] = other[0]);
|
other[1] > extent[1] && (extent[1] = other[1]);
|
|
// unionExtent may called by it's sub classes
|
IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]);
|
},
|
/**
|
* Get interval
|
*/
|
getInterval: function () {
|
if (!this._interval) {
|
this.niceTicks();
|
}
|
return this._interval;
|
},
|
|
/**
|
* Set interval
|
*/
|
setInterval: function (interval) {
|
this._interval = interval;
|
// Dropped auto calculated niceExtent and use user setted extent
|
// We assume user wan't to set both interval, min, max to get a better result
|
this._niceExtent = this._extent.slice();
|
},
|
|
/**
|
* @return {Array.<number>}
|
*/
|
getTicks: function () {
|
if (!this._interval) {
|
this.niceTicks();
|
}
|
return helper.intervalScaleGetTicks(
|
this._interval, this._extent, this._niceExtent, this._intervalPrecision
|
);
|
},
|
|
/**
|
* @return {Array.<string>}
|
*/
|
getTicksLabels: function () {
|
var labels = [];
|
var ticks = this.getTicks();
|
for (var i = 0; i < ticks.length; i++) {
|
labels.push(this.getLabel(ticks[i]));
|
}
|
return labels;
|
},
|
|
/**
|
* @param {number} data
|
* @param {Object} [opt]
|
* @param {number|string} [opt.precision] If 'auto', use nice presision.
|
* @param {boolean} [opt.pad] returns 1.50 but not 1.5 if precision is 2.
|
* @return {string}
|
*/
|
getLabel: function (data, opt) {
|
if (data == null) {
|
return '';
|
}
|
|
var precision = opt && opt.precision;
|
|
if (precision == null) {
|
precision = numberUtil.getPrecisionSafe(data) || 0;
|
}
|
else if (precision === 'auto') {
|
// Should be more precise then tick.
|
precision = this._intervalPrecision;
|
}
|
|
// (1) If `precision` is set, 12.005 should be display as '12.00500'.
|
// (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'.
|
data = roundNumber(data, precision, true);
|
|
return formatUtil.addCommas(data);
|
},
|
|
/**
|
* Update interval and extent of intervals for nice ticks
|
*
|
* @param {number} [splitNumber = 5] Desired number of ticks
|
*/
|
niceTicks: function (splitNumber) {
|
splitNumber = splitNumber || 5;
|
var extent = this._extent;
|
var span = extent[1] - extent[0];
|
if (!isFinite(span)) {
|
return;
|
}
|
// User may set axis min 0 and data are all negative
|
// FIXME If it needs to reverse ?
|
if (span < 0) {
|
span = -span;
|
extent.reverse();
|
}
|
|
var result = helper.intervalScaleNiceTicks(extent, splitNumber);
|
|
this._intervalPrecision = result.intervalPrecision;
|
this._interval = result.interval;
|
this._niceExtent = result.niceTickExtent;
|
},
|
|
/**
|
* Nice extent.
|
* @param {number} [splitNumber = 5] Given approx tick number
|
* @param {boolean} [fixMin=false]
|
* @param {boolean} [fixMax=false]
|
*/
|
niceExtent: function (splitNumber, fixMin, fixMax) {
|
var extent = this._extent;
|
// If extent start and end are same, expand them
|
if (extent[0] === extent[1]) {
|
if (extent[0] !== 0) {
|
// Expand extent
|
var expandSize = extent[0];
|
// In the fowllowing case
|
// Axis has been fixed max 100
|
// Plus data are all 100 and axis extent are [100, 100].
|
// Extend to the both side will cause expanded max is larger than fixed max.
|
// So only expand to the smaller side.
|
if (!fixMax) {
|
extent[1] += expandSize / 2;
|
extent[0] -= expandSize / 2;
|
}
|
else {
|
extent[0] -= expandSize / 2;
|
}
|
}
|
else {
|
extent[1] = 1;
|
}
|
}
|
var span = extent[1] - extent[0];
|
// If there are no data and extent are [Infinity, -Infinity]
|
if (!isFinite(span)) {
|
extent[0] = 0;
|
extent[1] = 1;
|
}
|
|
this.niceTicks(splitNumber);
|
|
// var extent = this._extent;
|
var interval = this._interval;
|
|
if (!fixMin) {
|
extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval);
|
}
|
if (!fixMax) {
|
extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval);
|
}
|
}
|
});
|
|
/**
|
* @return {module:echarts/scale/Time}
|
*/
|
IntervalScale.create = function () {
|
return new IntervalScale();
|
};
|
|
module.exports = IntervalScale;
|
|
|
|
/***/ },
|
/* 109 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* For testable.
|
*/
|
|
|
var numberUtil = __webpack_require__(7);
|
|
var roundNumber = numberUtil.round;
|
|
var helper = {};
|
|
/**
|
* @param {Array.<number>} extent Both extent[0] and extent[1] should be valid number.
|
* Should be extent[0] < extent[1].
|
* @param {number} splitNumber splitNumber should be >= 1.
|
* @return {Object} {interval, intervalPrecision, niceTickExtent}
|
*/
|
helper.intervalScaleNiceTicks = function (extent, splitNumber) {
|
var result = {};
|
var span = extent[1] - extent[0];
|
|
var interval = result.interval = numberUtil.nice(span / splitNumber, true);
|
// Tow more digital for tick.
|
var precision = result.intervalPrecision = numberUtil.getPrecisionSafe(interval) + 2;
|
// Niced extent inside original extent
|
var niceTickExtent = result.niceTickExtent = [
|
roundNumber(Math.ceil(extent[0] / interval) * interval, precision),
|
roundNumber(Math.floor(extent[1] / interval) * interval, precision)
|
];
|
|
helper.fixExtent(niceTickExtent, extent);
|
|
return result;
|
};
|
|
function clamp(niceTickExtent, idx, extent) {
|
niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]);
|
}
|
|
// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
|
helper.fixExtent = function (niceTickExtent, extent) {
|
!isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]);
|
!isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]);
|
clamp(niceTickExtent, 0, extent);
|
clamp(niceTickExtent, 1, extent);
|
if (niceTickExtent[0] > niceTickExtent[1]) {
|
niceTickExtent[0] = niceTickExtent[1];
|
}
|
};
|
|
helper.intervalScaleGetTicks = function (interval, extent, niceTickExtent, intervalPrecision) {
|
var ticks = [];
|
|
// If interval is 0, return [];
|
if (!interval) {
|
return ticks;
|
}
|
|
// Consider this case: using dataZoom toolbox, zoom and zoom.
|
var safeLimit = 10000;
|
|
if (extent[0] < niceTickExtent[0]) {
|
ticks.push(extent[0]);
|
}
|
var tick = niceTickExtent[0];
|
|
while (tick <= niceTickExtent[1]) {
|
ticks.push(tick);
|
// Avoid rounding error
|
tick = roundNumber(tick + interval, intervalPrecision);
|
if (tick === ticks[ticks.length - 1]) {
|
// Consider out of safe float point, e.g.,
|
// -3711126.9907707 + 2e-10 === -3711126.9907707
|
break;
|
}
|
if (ticks.length > safeLimit) {
|
return [];
|
}
|
}
|
// Consider this case: the last item of ticks is smaller
|
// than niceTickExtent[1] and niceTickExtent[1] === extent[1].
|
if (extent[1] > (ticks.length ? ticks[ticks.length - 1] : niceTickExtent[1])) {
|
ticks.push(extent[1]);
|
}
|
|
return ticks;
|
};
|
|
module.exports = helper;
|
|
|
/***/ },
|
/* 110 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Interval scale
|
* @module echarts/coord/scale/Time
|
*/
|
|
|
|
// [About UTC and local time zone]:
|
// In most cases, `number.parseDate` will treat input data string as local time
|
// (except time zone is specified in time string). And `format.formateTime` returns
|
// local time by default. option.useUTC is false by default. This design have
|
// concidered these common case:
|
// (1) Time that is persistent in server is in UTC, but it is needed to be diplayed
|
// in local time by default.
|
// (2) By default, the input data string (e.g., '2011-01-02') should be displayed
|
// as its original time, without any time difference.
|
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
var formatUtil = __webpack_require__(6);
|
var scaleHelper = __webpack_require__(109);
|
|
var IntervalScale = __webpack_require__(108);
|
|
var intervalScaleProto = IntervalScale.prototype;
|
|
var mathCeil = Math.ceil;
|
var mathFloor = Math.floor;
|
var ONE_SECOND = 1000;
|
var ONE_MINUTE = ONE_SECOND * 60;
|
var ONE_HOUR = ONE_MINUTE * 60;
|
var ONE_DAY = ONE_HOUR * 24;
|
|
// FIXME 公用?
|
var bisect = function (a, x, lo, hi) {
|
while (lo < hi) {
|
var mid = lo + hi >>> 1;
|
if (a[mid][2] < x) {
|
lo = mid + 1;
|
}
|
else {
|
hi = mid;
|
}
|
}
|
return lo;
|
};
|
|
/**
|
* @alias module:echarts/coord/scale/Time
|
* @constructor
|
*/
|
var TimeScale = IntervalScale.extend({
|
type: 'time',
|
|
// Overwrite
|
getLabel: function (val) {
|
var stepLvl = this._stepLvl;
|
|
var date = new Date(val);
|
|
return formatUtil.formatTime(stepLvl[0], date, this.getSetting('useUTC'));
|
},
|
|
// Overwrite
|
niceExtent: function (approxTickNum, fixMin, fixMax) {
|
var extent = this._extent;
|
// If extent start and end are same, expand them
|
if (extent[0] === extent[1]) {
|
// Expand extent
|
extent[0] -= ONE_DAY;
|
extent[1] += ONE_DAY;
|
}
|
// If there are no data and extent are [Infinity, -Infinity]
|
if (extent[1] === -Infinity && extent[0] === Infinity) {
|
var d = new Date();
|
extent[1] = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
extent[0] = extent[1] - ONE_DAY;
|
}
|
|
this.niceTicks(approxTickNum);
|
|
// var extent = this._extent;
|
var interval = this._interval;
|
|
if (!fixMin) {
|
extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval);
|
}
|
if (!fixMax) {
|
extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval);
|
}
|
},
|
|
// Overwrite
|
niceTicks: function (approxTickNum) {
|
var timezoneOffset = this.getSetting('useUTC')
|
? 0 : numberUtil.getTimezoneOffset() * 60 * 1000;
|
approxTickNum = approxTickNum || 10;
|
|
var extent = this._extent;
|
var span = extent[1] - extent[0];
|
var approxInterval = span / approxTickNum;
|
var scaleLevelsLen = scaleLevels.length;
|
var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen);
|
|
var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)];
|
var interval = level[2];
|
// Same with interval scale if span is much larger than 1 year
|
if (level[0] === 'year') {
|
var yearSpan = span / interval;
|
|
// From "Nice Numbers for Graph Labels" of Graphic Gems
|
// var niceYearSpan = numberUtil.nice(yearSpan, false);
|
var yearStep = numberUtil.nice(yearSpan / approxTickNum, true);
|
|
interval *= yearStep;
|
}
|
|
var niceExtent = [
|
Math.round(mathCeil((extent[0] - timezoneOffset) / interval) * interval + timezoneOffset),
|
Math.round(mathFloor((extent[1] - timezoneOffset)/ interval) * interval + timezoneOffset)
|
];
|
|
scaleHelper.fixExtent(niceExtent, extent);
|
|
this._stepLvl = level;
|
// Interval will be used in getTicks
|
this._interval = interval;
|
this._niceExtent = niceExtent;
|
},
|
|
parse: function (val) {
|
// val might be float.
|
return +numberUtil.parseDate(val);
|
}
|
});
|
|
zrUtil.each(['contain', 'normalize'], function (methodName) {
|
TimeScale.prototype[methodName] = function (val) {
|
return intervalScaleProto[methodName].call(this, this.parse(val));
|
};
|
});
|
|
// Steps from d3
|
var scaleLevels = [
|
// Format step interval
|
['hh:mm:ss', 1, ONE_SECOND], // 1s
|
['hh:mm:ss', 5, ONE_SECOND * 5], // 5s
|
['hh:mm:ss', 10, ONE_SECOND * 10], // 10s
|
['hh:mm:ss', 15, ONE_SECOND * 15], // 15s
|
['hh:mm:ss', 30, ONE_SECOND * 30], // 30s
|
['hh:mm\nMM-dd',1, ONE_MINUTE], // 1m
|
['hh:mm\nMM-dd',5, ONE_MINUTE * 5], // 5m
|
['hh:mm\nMM-dd',10, ONE_MINUTE * 10], // 10m
|
['hh:mm\nMM-dd',15, ONE_MINUTE * 15], // 15m
|
['hh:mm\nMM-dd',30, ONE_MINUTE * 30], // 30m
|
['hh:mm\nMM-dd',1, ONE_HOUR], // 1h
|
['hh:mm\nMM-dd',2, ONE_HOUR * 2], // 2h
|
['hh:mm\nMM-dd',6, ONE_HOUR * 6], // 6h
|
['hh:mm\nMM-dd',12, ONE_HOUR * 12], // 12h
|
['MM-dd\nyyyy', 1, ONE_DAY], // 1d
|
['week', 7, ONE_DAY * 7], // 7d
|
['month', 1, ONE_DAY * 31], // 1M
|
['quarter', 3, ONE_DAY * 380 / 4], // 3M
|
['half-year', 6, ONE_DAY * 380 / 2], // 6M
|
['year', 1, ONE_DAY * 380] // 1Y
|
];
|
|
/**
|
* @param {module:echarts/model/Model}
|
* @return {module:echarts/scale/Time}
|
*/
|
TimeScale.create = function (model) {
|
return new TimeScale({useUTC: model.ecModel.get('useUTC')});
|
};
|
|
module.exports = TimeScale;
|
|
|
/***/ },
|
/* 111 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Log scale
|
* @module echarts/scale/Log
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var Scale = __webpack_require__(107);
|
var numberUtil = __webpack_require__(7);
|
|
// Use some method of IntervalScale
|
var IntervalScale = __webpack_require__(108);
|
|
var scaleProto = Scale.prototype;
|
var intervalScaleProto = IntervalScale.prototype;
|
|
var getPrecisionSafe = numberUtil.getPrecisionSafe;
|
var roundingErrorFix = numberUtil.round;
|
|
var mathFloor = Math.floor;
|
var mathCeil = Math.ceil;
|
var mathPow = Math.pow;
|
|
var mathLog = Math.log;
|
|
var LogScale = Scale.extend({
|
|
type: 'log',
|
|
base: 10,
|
|
$constructor: function () {
|
Scale.apply(this, arguments);
|
this._originalScale = new IntervalScale();
|
},
|
|
/**
|
* @return {Array.<number>}
|
*/
|
getTicks: function () {
|
var originalScale = this._originalScale;
|
var extent = this._extent;
|
var originalExtent = originalScale.getExtent();
|
|
return zrUtil.map(intervalScaleProto.getTicks.call(this), function (val) {
|
var powVal = numberUtil.round(mathPow(this.base, val));
|
|
// Fix #4158
|
powVal = (val === extent[0] && originalScale.__fixMin)
|
? fixRoundingError(powVal, originalExtent[0])
|
: powVal;
|
powVal = (val === extent[1] && originalScale.__fixMax)
|
? fixRoundingError(powVal, originalExtent[1])
|
: powVal;
|
|
return powVal;
|
}, this);
|
},
|
|
/**
|
* @param {number} val
|
* @return {string}
|
*/
|
getLabel: intervalScaleProto.getLabel,
|
|
/**
|
* @param {number} val
|
* @return {number}
|
*/
|
scale: function (val) {
|
val = scaleProto.scale.call(this, val);
|
return mathPow(this.base, val);
|
},
|
|
/**
|
* @param {number} start
|
* @param {number} end
|
*/
|
setExtent: function (start, end) {
|
var base = this.base;
|
start = mathLog(start) / mathLog(base);
|
end = mathLog(end) / mathLog(base);
|
intervalScaleProto.setExtent.call(this, start, end);
|
},
|
|
/**
|
* @return {number} end
|
*/
|
getExtent: function () {
|
var base = this.base;
|
var extent = scaleProto.getExtent.call(this);
|
extent[0] = mathPow(base, extent[0]);
|
extent[1] = mathPow(base, extent[1]);
|
|
// Fix #4158
|
var originalScale = this._originalScale;
|
var originalExtent = originalScale.getExtent();
|
originalScale.__fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0]));
|
originalScale.__fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1]));
|
|
return extent;
|
},
|
|
/**
|
* @param {Array.<number>} extent
|
*/
|
unionExtent: function (extent) {
|
this._originalScale.unionExtent(extent);
|
|
var base = this.base;
|
extent[0] = mathLog(extent[0]) / mathLog(base);
|
extent[1] = mathLog(extent[1]) / mathLog(base);
|
scaleProto.unionExtent.call(this, extent);
|
},
|
|
/**
|
* @override
|
*/
|
unionExtentFromData: function (data, dim) {
|
this.unionExtent(data.getDataExtent(dim, true, function (val) {
|
return val > 0;
|
}));
|
},
|
|
/**
|
* Update interval and extent of intervals for nice ticks
|
* @param {number} [approxTickNum = 10] Given approx tick number
|
*/
|
niceTicks: function (approxTickNum) {
|
approxTickNum = approxTickNum || 10;
|
var extent = this._extent;
|
var span = extent[1] - extent[0];
|
if (span === Infinity || span <= 0) {
|
return;
|
}
|
|
var interval = numberUtil.quantity(span);
|
var err = approxTickNum / span * interval;
|
|
// Filter ticks to get closer to the desired count.
|
if (err <= 0.5) {
|
interval *= 10;
|
}
|
|
// Interval should be integer
|
while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) {
|
interval *= 10;
|
}
|
|
var niceExtent = [
|
numberUtil.round(mathCeil(extent[0] / interval) * interval),
|
numberUtil.round(mathFloor(extent[1] / interval) * interval)
|
];
|
|
this._interval = interval;
|
this._niceExtent = niceExtent;
|
},
|
|
/**
|
* Nice extent.
|
* @param {number} [approxTickNum = 10] Given approx tick number
|
* @param {boolean} [fixMin=false]
|
* @param {boolean} [fixMax=false]
|
*/
|
niceExtent: function (splitNumber, fixMin, fixMax) {
|
intervalScaleProto.niceExtent.call(this, splitNumber, fixMin, fixMax);
|
|
var originalScale = this._originalScale;
|
originalScale.__fixMin = fixMin;
|
originalScale.__fixMax = fixMax;
|
}
|
|
});
|
|
zrUtil.each(['contain', 'normalize'], function (methodName) {
|
LogScale.prototype[methodName] = function (val) {
|
val = mathLog(val) / mathLog(this.base);
|
return scaleProto[methodName].call(this, val);
|
};
|
});
|
|
LogScale.create = function () {
|
return new LogScale();
|
};
|
|
function fixRoundingError(val, originalVal) {
|
return roundingErrorFix(val, getPrecisionSafe(originalVal));
|
}
|
|
module.exports = LogScale;
|
|
|
/***/ },
|
/* 112 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var axisHelper = __webpack_require__(105);
|
|
function getName(obj) {
|
if (zrUtil.isObject(obj) && obj.value != null) {
|
return obj.value;
|
}
|
else {
|
return obj;
|
}
|
}
|
|
module.exports = {
|
|
/**
|
* Format labels
|
* @return {Array.<string>}
|
*/
|
getFormattedLabels: function () {
|
return axisHelper.getFormattedLabels(
|
this.axis,
|
this.get('axisLabel.formatter')
|
);
|
},
|
|
/**
|
* Get categories
|
*/
|
getCategories: function () {
|
return this.get('type') === 'category'
|
&& zrUtil.map(this.get('data'), getName);
|
},
|
|
/**
|
* @param {boolean} origin
|
* @return {number|string} min value or 'dataMin' or null/undefined (means auto) or NaN
|
*/
|
getMin: function (origin) {
|
var option = this.option;
|
var min = (!origin && option.rangeStart != null)
|
? option.rangeStart : option.min;
|
|
if (this.axis && min != null && min !== 'dataMin' && !zrUtil.eqNaN(min)) {
|
min = this.axis.scale.parse(min);
|
}
|
return min;
|
},
|
|
/**
|
* @param {boolean} origin
|
* @return {number|string} max value or 'dataMax' or null/undefined (means auto) or NaN
|
*/
|
getMax: function (origin) {
|
var option = this.option;
|
var max = (!origin && option.rangeEnd != null)
|
? option.rangeEnd : option.max;
|
|
if (this.axis && max != null && max !== 'dataMax' && !zrUtil.eqNaN(max)) {
|
max = this.axis.scale.parse(max);
|
}
|
return max;
|
},
|
|
/**
|
* @return {boolean}
|
*/
|
getNeedCrossZero: function () {
|
var option = this.option;
|
return (option.rangeStart != null || option.rangeEnd != null)
|
? false : !option.scale;
|
},
|
|
/**
|
* Should be implemented by each axis model if necessary.
|
* @return {module:echarts/model/Component} coordinate system model
|
*/
|
getCoordSysModel: zrUtil.noop,
|
|
/**
|
* @param {number} rangeStart Can only be finite number or null/undefined or NaN.
|
* @param {number} rangeEnd Can only be finite number or null/undefined or NaN.
|
*/
|
setRange: function (rangeStart, rangeEnd) {
|
this.option.rangeStart = rangeStart;
|
this.option.rangeEnd = rangeEnd;
|
},
|
|
/**
|
* Reset range
|
*/
|
resetRange: function () {
|
// rangeStart and rangeEnd is readonly.
|
this.option.rangeStart = this.option.rangeEnd = null;
|
}
|
};
|
|
|
/***/ },
|
/* 113 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var echarts = __webpack_require__(1);
|
var PRIORITY = echarts.PRIORITY;
|
|
__webpack_require__(114);
|
__webpack_require__(115);
|
|
echarts.registerVisual(zrUtil.curry(
|
__webpack_require__(120), 'line', 'circle', 'line'
|
));
|
echarts.registerLayout(zrUtil.curry(
|
__webpack_require__(121), 'line'
|
));
|
|
// Down sample after filter
|
echarts.registerProcessor(PRIORITY.PROCESSOR.STATISTIC, zrUtil.curry(
|
__webpack_require__(122), 'line'
|
));
|
|
// In case developer forget to include grid component
|
__webpack_require__(123);
|
|
|
/***/ },
|
/* 114 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var createListFromArray = __webpack_require__(102);
|
var SeriesModel = __webpack_require__(28);
|
|
module.exports = SeriesModel.extend({
|
|
type: 'series.line',
|
|
dependencies: ['grid', 'polar'],
|
|
getInitialData: function (option, ecModel) {
|
if (true) {
|
var coordSys = option.coordinateSystem;
|
if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
|
throw new Error('Line not support coordinateSystem besides cartesian and polar');
|
}
|
}
|
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: {},
|
// false, 'start', 'end', 'middle'
|
step: false,
|
|
// Disabled if step is true
|
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',
|
|
// Disable progressive
|
progressive: 0,
|
hoverLayerThreshold: Infinity
|
}
|
});
|
|
|
/***/ },
|
/* 115 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// FIXME step not support polar
|
|
|
var zrUtil = __webpack_require__(4);
|
var SymbolDraw = __webpack_require__(116);
|
var Symbol = __webpack_require__(117);
|
var lineAnimationDiff = __webpack_require__(118);
|
var graphic = __webpack_require__(44);
|
var modelUtil = __webpack_require__(5);
|
var polyHelper = __webpack_require__(119);
|
var ChartView = __webpack_require__(43);
|
|
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 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);
|
}
|
|
function turnPointsIntoStep(points, coordSys, stepTurnAt) {
|
var baseAxis = coordSys.getBaseAxis();
|
var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1;
|
|
var stepPoints = [];
|
for (var i = 0; i < points.length - 1; i++) {
|
var nextPt = points[i + 1];
|
var pt = points[i];
|
stepPoints.push(pt);
|
|
var stepPt = [];
|
switch (stepTurnAt) {
|
case 'end':
|
stepPt[baseIndex] = nextPt[baseIndex];
|
stepPt[1 - baseIndex] = pt[1 - baseIndex];
|
// default is start
|
stepPoints.push(stepPt);
|
break;
|
case 'middle':
|
// default is start
|
var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
|
var stepPt2 = [];
|
stepPt[baseIndex] = stepPt2[baseIndex] = middle;
|
stepPt[1 - baseIndex] = pt[1 - baseIndex];
|
stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
|
stepPoints.push(stepPt);
|
stepPoints.push(stepPt2);
|
break;
|
default:
|
stepPt[baseIndex] = pt[baseIndex];
|
stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
|
// default is start
|
stepPoints.push(stepPt);
|
}
|
}
|
// Last points
|
points[i] && stepPoints.push(points[i]);
|
return stepPoints;
|
}
|
|
function getVisualGradient(data, coordSys) {
|
var visualMetaList = data.getVisual('visualMeta');
|
if (!visualMetaList || !visualMetaList.length || !data.count()) {
|
// When data.count() is 0, gradient range can not be calculated.
|
return;
|
}
|
|
var visualMeta;
|
for (var i = visualMetaList.length - 1; i >= 0; i--) {
|
// Can only be x or y
|
if (visualMetaList[i].dimension < 2) {
|
visualMeta = visualMetaList[i];
|
break;
|
}
|
}
|
if (!visualMeta || coordSys.type !== 'cartesian2d') {
|
if (true) {
|
console.warn('Visual map on line style only support x or y dimension.');
|
}
|
return;
|
}
|
|
// If the area to be rendered is bigger than area defined by LinearGradient,
|
// the canvas spec prescribes that the color of the first stop and the last
|
// stop should be used. But if two stops are added at offset 0, in effect
|
// browsers use the color of the second stop to render area outside
|
// LinearGradient. So we can only infinitesimally extend area defined in
|
// LinearGradient to render `outerColors`.
|
|
var dimension = visualMeta.dimension;
|
var dimName = data.dimensions[dimension];
|
var axis = coordSys.getAxis(dimName);
|
|
// dataToCoor mapping may not be linear, but must be monotonic.
|
var colorStops = zrUtil.map(visualMeta.stops, function (stop) {
|
return {
|
coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)),
|
color: stop.color
|
};
|
});
|
var stopLen = colorStops.length;
|
var outerColors = visualMeta.outerColors.slice();
|
|
if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) {
|
colorStops.reverse();
|
outerColors.reverse();
|
}
|
|
var tinyExtent = 10; // Arbitrary value: 10px
|
var minCoord = colorStops[0].coord - tinyExtent;
|
var maxCoord = colorStops[stopLen - 1].coord + tinyExtent;
|
var coordSpan = maxCoord - minCoord;
|
|
if (coordSpan < 1e-3) {
|
return 'transparent';
|
}
|
|
zrUtil.each(colorStops, function (stop) {
|
stop.offset = (stop.coord - minCoord) / coordSpan;
|
});
|
colorStops.push({
|
offset: stopLen ? colorStops[stopLen - 1].offset : 0.5,
|
color: outerColors[1] || 'transparent'
|
});
|
colorStops.unshift({ // notice colorStops.length have been changed.
|
offset: stopLen ? colorStops[0].offset : 0.5,
|
color: outerColors[0] || 'transparent'
|
});
|
|
// zrUtil.each(colorStops, function (colorStop) {
|
// // Make sure each offset has rounded px to avoid not sharp edge
|
// colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start);
|
// });
|
|
var gradient = new graphic.LinearGradient(0, 0, 0, 0, colorStops, true);
|
gradient[dimName] = minCoord;
|
gradient[dimName + '2'] = maxCoord;
|
|
return gradient;
|
}
|
|
module.exports = 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);
|
|
// FIXME step not support polar
|
var step = !isCoordSysPolar && seriesModel.get('step');
|
// Initialization animation or coordinate system changed
|
if (
|
!(polyline && prevCoordSys.type === coordSys.type && step === this._step)
|
) {
|
showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
|
|
if (step) {
|
// TODO If stacked series is not step
|
points = turnPointsIntoStep(points, coordSys, step);
|
stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
|
}
|
|
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, step
|
);
|
}
|
else {
|
// Not do it in update with animation
|
if (step) {
|
// TODO If stacked series is not step
|
points = turnPointsIntoStep(points, coordSys, step);
|
stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step);
|
}
|
|
polyline.setShape({
|
points: points
|
});
|
polygon && polygon.setShape({
|
points: points,
|
stackedOnPoints: stackedOnPoints
|
});
|
}
|
}
|
}
|
|
var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color');
|
|
polyline.useStyle(zrUtil.defaults(
|
// Use color in lineStyle first
|
lineStyleModel.getLineStyle(),
|
{
|
fill: 'none',
|
stroke: visualColor,
|
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: visualColor,
|
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;
|
this._step = step;
|
},
|
|
dispose: function () {},
|
|
highlight: function (seriesModel, ecModel, api, payload) {
|
var data = seriesModel.getData();
|
var dataIndex = modelUtil.queryDataIndex(data, payload);
|
|
if (!(dataIndex instanceof Array) && 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);
|
if (!pt) {
|
// Null data
|
return;
|
}
|
symbol = new Symbol(data, dataIndex);
|
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 = modelUtil.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 {
|
// FIXME
|
// can not downplay completely.
|
// 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, step) {
|
var polyline = this._polyline;
|
var polygon = this._polygon;
|
var seriesModel = data.hostModel;
|
|
var diff = lineAnimationDiff(
|
this._data, data,
|
this._stackedOnPoints, stackedOnPoints,
|
this._coordSys, coordSys
|
);
|
|
var current = diff.current;
|
var stackedOnCurrent = diff.stackedOnCurrent;
|
var next = diff.next;
|
var stackedOnNext = diff.stackedOnNext;
|
if (step) {
|
// TODO If stacked series is not step
|
current = turnPointsIntoStep(diff.current, coordSys, step);
|
stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step);
|
next = turnPointsIntoStep(diff.next, coordSys, step);
|
stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
|
}
|
// `diff.current` is subset of `current` (which should be ensured by
|
// turnPointsIntoStep), so points in `__points` can be updated when
|
// points in `current` are update during animation.
|
polyline.shape.__points = diff.current;
|
polyline.shape.points = current;
|
|
graphic.updateProps(polyline, {
|
shape: {
|
points: next
|
}
|
}, seriesModel);
|
|
if (polygon) {
|
polygon.setShape({
|
points: current,
|
stackedOnPoints: stackedOnCurrent
|
});
|
graphic.updateProps(polygon, {
|
shape: {
|
points: next,
|
stackedOnPoints: 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;
|
}
|
});
|
|
|
/***/ },
|
/* 116 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/chart/helper/SymbolDraw
|
*/
|
|
|
var graphic = __webpack_require__(44);
|
var Symbol = __webpack_require__(117);
|
|
/**
|
* @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);
|
// Is an object
|
// if (point && point.hasOwnProperty('point')) {
|
// point = point.point;
|
// }
|
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;
|
|
var seriesScope = {
|
itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']),
|
hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(),
|
symbolRotate: seriesModel.get('symbolRotate'),
|
symbolOffset: seriesModel.get('symbolOffset'),
|
hoverAnimation: seriesModel.get('hoverAnimation'),
|
|
labelModel: seriesModel.getModel('label.normal'),
|
hoverLabelModel: seriesModel.getModel('label.emphasis')
|
};
|
|
data.diff(oldData)
|
.add(function (newIdx) {
|
var point = data.getItemLayout(newIdx);
|
if (symbolNeedsDraw(data, newIdx, isIgnore)) {
|
var symbolEl = new SymbolCtor(data, newIdx, seriesScope);
|
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, seriesScope);
|
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) {
|
var point = data.getItemLayout(idx);
|
el.attr('position', point);
|
});
|
}
|
};
|
|
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();
|
}
|
}
|
};
|
|
module.exports = SymbolDraw;
|
|
|
/***/ },
|
/* 117 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/chart/helper/Symbol
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var symbolUtil = __webpack_require__(104);
|
var graphic = __webpack_require__(44);
|
var numberUtil = __webpack_require__(7);
|
|
function getSymbolSize(data, idx) {
|
var symbolSize = data.getItemVisual(idx, 'symbolSize');
|
return symbolSize instanceof Array
|
? symbolSize.slice()
|
: [+symbolSize, +symbolSize];
|
}
|
|
function getScale(symbolSize) {
|
return [symbolSize[0] / 2, symbolSize[1] / 2];
|
}
|
|
/**
|
* @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, seriesScope) {
|
graphic.Group.call(this);
|
|
this.updateData(data, idx, seriesScope);
|
}
|
|
var symbolProto = Symbol.prototype;
|
|
function driftSymbol(dx, dy) {
|
this.parent.drift(dx, dy);
|
}
|
|
symbolProto._createSymbol = function (symbolType, data, idx, symbolSize) {
|
// 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
|
// );
|
// If width/height are set too small (e.g., set to 1) on ios10
|
// and macOS Sierra, a circle stroke become a rect, no matter what
|
// the scale is set. So we set width/height as 2. See #4150.
|
var symbolPath = symbolUtil.createSymbol(
|
symbolType, -1, -1, 2, 2, color
|
);
|
|
symbolPath.attr({
|
z2: 100,
|
culling: true,
|
scale: [0, 0]
|
});
|
// Rewrite drift method
|
symbolPath.drift = driftSymbol;
|
|
graphic.initProps(symbolPath, {
|
scale: getScale(symbolSize)
|
}, seriesModel, idx);
|
this._symbolType = symbolType;
|
|
this.add(symbolPath);
|
};
|
|
/**
|
* Stop animation
|
* @param {boolean} toLastFrame
|
*/
|
symbolProto.stopSymbolAnimation = function (toLastFrame) {
|
this.childAt(0).stopAnimation(toLastFrame);
|
};
|
|
/**
|
* Get symbol path element
|
*/
|
symbolProto.getSymbolPath = function () {
|
return this.childAt(0);
|
};
|
|
/**
|
* 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, seriesScope) {
|
this.silent = false;
|
|
var symbolType = data.getItemVisual(idx, 'symbol') || 'circle';
|
var seriesModel = data.hostModel;
|
var symbolSize = getSymbolSize(data, idx);
|
|
if (symbolType !== this._symbolType) {
|
this._createSymbol(symbolType, data, idx, symbolSize);
|
}
|
else {
|
var symbolPath = this.childAt(0);
|
symbolPath.silent = false;
|
graphic.updateProps(symbolPath, {
|
scale: getScale(symbolSize)
|
}, seriesModel, idx);
|
}
|
this._updateCommon(data, idx, symbolSize, seriesScope);
|
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, seriesScope) {
|
var symbolPath = this.childAt(0);
|
var seriesModel = data.hostModel;
|
var color = data.getItemVisual(idx, 'color');
|
|
// Reset style
|
if (symbolPath.type !== 'image') {
|
symbolPath.useStyle({
|
strokeNoScale: true
|
});
|
}
|
|
seriesScope = seriesScope || null;
|
|
var itemStyle = seriesScope && seriesScope.itemStyle;
|
var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle;
|
var symbolRotate = seriesScope && seriesScope.symbolRotate;
|
var symbolOffset = seriesScope && seriesScope.symbolOffset;
|
var labelModel = seriesScope && seriesScope.labelModel;
|
var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
|
var hoverAnimation = seriesScope && seriesScope.hoverAnimation;
|
|
if (!seriesScope || data.hasItemOption) {
|
var itemModel = data.getItemModel(idx);
|
|
// Color must be excluded.
|
// Because symbol provide setColor individually to set fill and stroke
|
itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']);
|
hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle();
|
|
symbolRotate = itemModel.getShallow('symbolRotate');
|
symbolOffset = itemModel.getShallow('symbolOffset');
|
|
labelModel = itemModel.getModel(normalLabelAccessPath);
|
hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath);
|
hoverAnimation = itemModel.getShallow('hoverAnimation');
|
}
|
else {
|
hoverItemStyle = zrUtil.extend({}, hoverItemStyle);
|
}
|
|
var elStyle = symbolPath.style;
|
|
symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
|
|
if (symbolOffset) {
|
symbolPath.attr('position', [
|
numberUtil.parsePercent(symbolOffset[0], symbolSize[0]),
|
numberUtil.parsePercent(symbolOffset[1], symbolSize[1])
|
]);
|
}
|
|
// PENDING setColor before setStyle!!!
|
symbolPath.setColor(color);
|
|
symbolPath.setStyle(itemStyle);
|
|
var opacity = data.getItemVisual(idx, 'opacity');
|
if (opacity != null) {
|
elStyle.opacity = opacity;
|
}
|
|
// 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.getShallow('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(hoverItemStyle, hoverLabelModel, color);
|
hoverItemStyle.text = zrUtil.retrieve(
|
seriesModel.getFormattedLabel(idx, 'emphasis'),
|
data.get(valueDim, idx)
|
);
|
}
|
else {
|
hoverItemStyle.text = '';
|
}
|
|
symbolPath.off('mouseover')
|
.off('mouseout')
|
.off('emphasis')
|
.off('normal');
|
|
symbolPath.hoverStyle = hoverItemStyle;
|
|
graphic.setHoverStyle(symbolPath);
|
|
var scale = getScale(symbolSize);
|
|
if (hoverAnimation && seriesModel.isAnimationEnabled()) {
|
var onEmphasis = function() {
|
var ratio = scale[1] / scale[0];
|
this.animateTo({
|
scale: [
|
Math.max(scale[0] * 1.1, scale[0] + 3),
|
Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
|
]
|
}, 400, 'elasticOut');
|
};
|
var onNormal = function() {
|
this.animateTo({
|
scale: scale
|
}, 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 mistaken hover when fading out
|
this.silent = symbolPath.silent = true;
|
// Not show text when animating
|
symbolPath.style.text = '';
|
graphic.updateProps(symbolPath, {
|
scale: [0, 0]
|
}, this._seriesModel, this.dataIndex, cb);
|
};
|
|
zrUtil.inherits(Symbol, graphic.Group);
|
|
module.exports = Symbol;
|
|
|
/***/ },
|
/* 118 */
|
/***/ function(module, exports) {
|
|
|
|
// var arrayDiff = require('zrender/lib/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;
|
}
|
|
module.exports = 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
|
};
|
};
|
|
|
/***/ },
|
/* 119 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// Poly path support NaN point
|
|
|
var Path = __webpack_require__(46);
|
var vec2 = __webpack_require__(10);
|
|
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
|
};
|
}
|
|
module.exports = {
|
|
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();
|
}
|
}
|
})
|
};
|
|
|
/***/ },
|
/* 120 */
|
/***/ function(module, exports) {
|
|
|
|
module.exports = function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) {
|
|
// Encoding visual for all series include which is filtered for legend drawing
|
ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
|
var data = seriesModel.getData();
|
|
var symbolType = seriesModel.get('symbol') || defaultSymbolType;
|
var symbolSize = seriesModel.get('symbolSize');
|
|
data.setVisual({
|
legendSymbol: legendSymbol || symbolType,
|
symbol: symbolType,
|
symbolSize: symbolSize
|
});
|
|
// Only visible series has each data be visual encoded
|
if (!ecModel.isSeriesFiltered(seriesModel)) {
|
if (typeof symbolSize === 'function') {
|
data.each(function (idx) {
|
var rawValue = seriesModel.getRawValue(idx);
|
// FIXME
|
var params = seriesModel.getDataParams(idx);
|
data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params));
|
});
|
}
|
data.each(function (idx) {
|
var itemModel = data.getItemModel(idx);
|
var itemSymbolType = itemModel.getShallow('symbol', true);
|
var itemSymbolSize = itemModel.getShallow('symbolSize', true);
|
// If has item symbol
|
if (itemSymbolType != null) {
|
data.setItemVisual(idx, 'symbol', itemSymbolType);
|
}
|
if (itemSymbolSize != null) {
|
// PENDING Transform symbolSize ?
|
data.setItemVisual(idx, 'symbolSize', itemSymbolSize);
|
}
|
});
|
}
|
});
|
};
|
|
|
/***/ },
|
/* 121 */
|
/***/ function(module, exports) {
|
|
|
|
module.exports = function (seriesType, ecModel) {
|
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
|
var data = seriesModel.getData();
|
var coordSys = seriesModel.coordinateSystem;
|
|
if (coordSys) {
|
var dims = coordSys.dimensions;
|
|
if (dims.length === 1) {
|
data.each(dims[0], function (x, idx) {
|
// Also {Array.<number>}, not undefined to avoid if...else... statement
|
data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x));
|
});
|
}
|
else if (dims.length === 2) {
|
data.each(dims, function (x, y, idx) {
|
// Also {Array.<number>}, not undefined to avoid if...else... statement
|
data.setItemLayout(
|
idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y])
|
);
|
}, true);
|
}
|
}
|
});
|
};
|
|
|
|
/***/ },
|
/* 122 */
|
/***/ function(module, exports) {
|
|
|
var samplers = {
|
average: function (frame) {
|
var sum = 0;
|
var count = 0;
|
for (var i = 0; i < frame.length; i++) {
|
if (!isNaN(frame[i])) {
|
sum += frame[i];
|
count++;
|
}
|
}
|
// Return NaN if count is 0
|
return count === 0 ? NaN : sum / count;
|
},
|
sum: function (frame) {
|
var sum = 0;
|
for (var i = 0; i < frame.length; i++) {
|
// Ignore NaN
|
sum += frame[i] || 0;
|
}
|
return sum;
|
},
|
max: function (frame) {
|
var max = -Infinity;
|
for (var i = 0; i < frame.length; i++) {
|
frame[i] > max && (max = frame[i]);
|
}
|
return max;
|
},
|
min: function (frame) {
|
var min = Infinity;
|
for (var i = 0; i < frame.length; i++) {
|
frame[i] < min && (min = frame[i]);
|
}
|
return min;
|
},
|
// TODO
|
// Median
|
nearest: function (frame) {
|
return frame[0];
|
}
|
};
|
|
var indexSampler = function (frame, value) {
|
return Math.round(frame.length / 2);
|
};
|
module.exports = function (seriesType, ecModel, api) {
|
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
|
var data = seriesModel.getData();
|
var sampling = seriesModel.get('sampling');
|
var coordSys = seriesModel.coordinateSystem;
|
// Only cartesian2d support down sampling
|
if (coordSys.type === 'cartesian2d' && sampling) {
|
var baseAxis = coordSys.getBaseAxis();
|
var valueAxis = coordSys.getOtherAxis(baseAxis);
|
var extent = baseAxis.getExtent();
|
// Coordinste system has been resized
|
var size = extent[1] - extent[0];
|
var rate = Math.round(data.count() / size);
|
if (rate > 1) {
|
var sampler;
|
if (typeof sampling === 'string') {
|
sampler = samplers[sampling];
|
}
|
else if (typeof sampling === 'function') {
|
sampler = sampling;
|
}
|
if (sampler) {
|
data = data.downSample(
|
valueAxis.dim, 1 / rate, sampler, indexSampler
|
);
|
seriesModel.setData(data);
|
}
|
}
|
}
|
}, this);
|
};
|
|
|
/***/ },
|
/* 123 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var graphic = __webpack_require__(44);
|
var zrUtil = __webpack_require__(4);
|
var echarts = __webpack_require__(1);
|
|
__webpack_require__(124);
|
|
__webpack_require__(133);
|
|
// Grid view
|
echarts.extendComponentView({
|
|
type: 'grid',
|
|
render: function (gridModel, ecModel) {
|
this.group.removeAll();
|
if (gridModel.get('show')) {
|
this.group.add(new graphic.Rect({
|
shape: gridModel.coordinateSystem.getRect(),
|
style: zrUtil.defaults({
|
fill: gridModel.get('backgroundColor')
|
}, gridModel.getItemStyle()),
|
silent: true,
|
z2: -1
|
}));
|
}
|
}
|
|
});
|
|
echarts.registerPreprocessor(function (option) {
|
// Only create grid when need
|
if (option.xAxis && option.yAxis && !option.grid) {
|
option.grid = {};
|
}
|
});
|
|
|
/***/ },
|
/* 124 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Grid is a region which contains at most 4 cartesian systems
|
*
|
* TODO Default cartesian
|
*/
|
var factory = exports;
|
|
var layout = __webpack_require__(21);
|
var axisHelper = __webpack_require__(105);
|
|
var zrUtil = __webpack_require__(4);
|
var Cartesian2D = __webpack_require__(125);
|
var Axis2D = __webpack_require__(127);
|
|
var each = zrUtil.each;
|
|
var ifAxisCrossZero = axisHelper.ifAxisCrossZero;
|
var niceScaleExtent = axisHelper.niceScaleExtent;
|
|
// 依赖 GridModel, AxisModel 做预处理
|
__webpack_require__(129);
|
|
/**
|
* Check if the axis is used in the specified grid
|
* @inner
|
*/
|
function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) {
|
return axisModel.getCoordSysModel() === gridModel;
|
}
|
|
function getLabelUnionRect(axis) {
|
var axisModel = axis.model;
|
var labels = axisModel.getFormattedLabels();
|
var textStyleModel = axisModel.getModel('axisLabel.textStyle');
|
var rect;
|
var step = 1;
|
var labelCount = labels.length;
|
if (labelCount > 40) {
|
// Simple optimization for large amount of labels
|
step = Math.ceil(labelCount / 40);
|
}
|
for (var i = 0; i < labelCount; i += step) {
|
if (!axis.isLabelIgnored(i)) {
|
var singleRect = textStyleModel.getTextRect(labels[i]);
|
// FIXME consider label rotate
|
rect ? rect.union(singleRect) : (rect = singleRect);
|
}
|
}
|
return rect;
|
}
|
|
function Grid(gridModel, ecModel, api) {
|
/**
|
* @type {Object.<string, module:echarts/coord/cartesian/Cartesian2D>}
|
* @private
|
*/
|
this._coordsMap = {};
|
|
/**
|
* @type {Array.<module:echarts/coord/cartesian/Cartesian>}
|
* @private
|
*/
|
this._coordsList = [];
|
|
/**
|
* @type {Object.<string, module:echarts/coord/cartesian/Axis2D>}
|
* @private
|
*/
|
this._axesMap = {};
|
|
/**
|
* @type {Array.<module:echarts/coord/cartesian/Axis2D>}
|
* @private
|
*/
|
this._axesList = [];
|
|
this._initCartesian(gridModel, ecModel, api);
|
|
this.model = gridModel;
|
}
|
|
var gridProto = Grid.prototype;
|
|
gridProto.type = 'grid';
|
|
gridProto.axisPointerEnabled = true;
|
|
gridProto.getRect = function () {
|
return this._rect;
|
};
|
|
gridProto.update = function (ecModel, api) {
|
|
var axesMap = this._axesMap;
|
|
this._updateScale(ecModel, this.model);
|
|
function ifAxisCanNotOnZero(otherAxisDim) {
|
var axes = axesMap[otherAxisDim];
|
for (var idx in axes) {
|
if (axes.hasOwnProperty(idx)) {
|
var axis = axes[idx];
|
if (axis && (axis.type === 'category' || !ifAxisCrossZero(axis))) {
|
return true;
|
}
|
}
|
}
|
return false;
|
}
|
|
each(axesMap.x, function (xAxis) {
|
niceScaleExtent(xAxis.scale, xAxis.model);
|
});
|
each(axesMap.y, function (yAxis) {
|
niceScaleExtent(yAxis.scale, yAxis.model);
|
});
|
// Fix configuration
|
each(axesMap.x, function (xAxis) {
|
// onZero can not be enabled in these two situations
|
// 1. When any other axis is a category axis
|
// 2. When any other axis not across 0 point
|
if (ifAxisCanNotOnZero('y')) {
|
xAxis.onZero = false;
|
}
|
});
|
each(axesMap.y, function (yAxis) {
|
if (ifAxisCanNotOnZero('x')) {
|
yAxis.onZero = false;
|
}
|
});
|
|
// Resize again if containLabel is enabled
|
// FIXME It may cause getting wrong grid size in data processing stage
|
this.resize(this.model, api);
|
};
|
|
/**
|
* Resize the grid
|
* @param {module:echarts/coord/cartesian/GridModel} gridModel
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
gridProto.resize = function (gridModel, api) {
|
|
var gridRect = layout.getLayoutRect(
|
gridModel.getBoxLayoutParams(), {
|
width: api.getWidth(),
|
height: api.getHeight()
|
});
|
|
this._rect = gridRect;
|
|
var axesList = this._axesList;
|
|
adjustAxes();
|
|
// Minus label size
|
if (gridModel.get('containLabel')) {
|
each(axesList, function (axis) {
|
if (!axis.model.get('axisLabel.inside')) {
|
var labelUnionRect = getLabelUnionRect(axis);
|
if (labelUnionRect) {
|
var dim = axis.isHorizontal() ? 'height' : 'width';
|
var margin = axis.model.get('axisLabel.margin');
|
gridRect[dim] -= labelUnionRect[dim] + margin;
|
if (axis.position === 'top') {
|
gridRect.y += labelUnionRect.height + margin;
|
}
|
else if (axis.position === 'left') {
|
gridRect.x += labelUnionRect.width + margin;
|
}
|
}
|
}
|
});
|
|
adjustAxes();
|
}
|
|
function adjustAxes() {
|
each(axesList, function (axis) {
|
var isHorizontal = axis.isHorizontal();
|
var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height];
|
var idx = axis.inverse ? 1 : 0;
|
axis.setExtent(extent[idx], extent[1 - idx]);
|
updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y);
|
});
|
}
|
};
|
|
/**
|
* @param {string} axisType
|
* @param {ndumber} [axisIndex]
|
*/
|
gridProto.getAxis = function (axisType, axisIndex) {
|
var axesMapOnDim = this._axesMap[axisType];
|
if (axesMapOnDim != null) {
|
if (axisIndex == null) {
|
// Find first axis
|
for (var name in axesMapOnDim) {
|
if (axesMapOnDim.hasOwnProperty(name)) {
|
return axesMapOnDim[name];
|
}
|
}
|
}
|
return axesMapOnDim[axisIndex];
|
}
|
};
|
|
/**
|
* @return {Array.<module:echarts/coord/Axis>}
|
*/
|
gridProto.getAxes = function () {
|
return this._axesList.slice();
|
};
|
|
/**
|
* Usage:
|
* grid.getCartesian(xAxisIndex, yAxisIndex);
|
* grid.getCartesian(xAxisIndex);
|
* grid.getCartesian(null, yAxisIndex);
|
* grid.getCartesian({xAxisIndex: ..., yAxisIndex: ...});
|
*
|
* @param {number|Object} [xAxisIndex]
|
* @param {number} [yAxisIndex]
|
*/
|
gridProto.getCartesian = function (xAxisIndex, yAxisIndex) {
|
if (xAxisIndex != null && yAxisIndex != null) {
|
var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
|
return this._coordsMap[key];
|
}
|
|
if (zrUtil.isObject(xAxisIndex)) {
|
yAxisIndex = xAxisIndex.yAxisIndex;
|
xAxisIndex = xAxisIndex.xAxisIndex;
|
}
|
// When only xAxisIndex or yAxisIndex given, find its first cartesian.
|
for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) {
|
if (coordList[i].getAxis('x').index === xAxisIndex
|
|| coordList[i].getAxis('y').index === yAxisIndex
|
) {
|
return coordList[i];
|
}
|
}
|
};
|
|
gridProto.getCartesians = function () {
|
return this._coordsList.slice();
|
};
|
|
/**
|
* @implements
|
* see {module:echarts/CoodinateSystem}
|
*/
|
gridProto.convertToPixel = function (ecModel, finder, value) {
|
var target = this._findConvertTarget(ecModel, finder);
|
|
return target.cartesian
|
? target.cartesian.dataToPoint(value)
|
: target.axis
|
? target.axis.toGlobalCoord(target.axis.dataToCoord(value))
|
: null;
|
};
|
|
/**
|
* @implements
|
* see {module:echarts/CoodinateSystem}
|
*/
|
gridProto.convertFromPixel = function (ecModel, finder, value) {
|
var target = this._findConvertTarget(ecModel, finder);
|
|
return target.cartesian
|
? target.cartesian.pointToData(value)
|
: target.axis
|
? target.axis.coordToData(target.axis.toLocalCoord(value))
|
: null;
|
};
|
|
/**
|
* @inner
|
*/
|
gridProto._findConvertTarget = function (ecModel, finder) {
|
var seriesModel = finder.seriesModel;
|
var xAxisModel = finder.xAxisModel
|
|| (seriesModel && seriesModel.getReferringComponents('xAxis')[0]);
|
var yAxisModel = finder.yAxisModel
|
|| (seriesModel && seriesModel.getReferringComponents('yAxis')[0]);
|
var gridModel = finder.gridModel;
|
var coordsList = this._coordsList;
|
var cartesian;
|
var axis;
|
|
if (seriesModel) {
|
cartesian = seriesModel.coordinateSystem;
|
zrUtil.indexOf(coordsList, cartesian) < 0 && (cartesian = null);
|
}
|
else if (xAxisModel && yAxisModel) {
|
cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex);
|
}
|
else if (xAxisModel) {
|
axis = this.getAxis('x', xAxisModel.componentIndex);
|
}
|
else if (yAxisModel) {
|
axis = this.getAxis('y', yAxisModel.componentIndex);
|
}
|
// Lowest priority.
|
else if (gridModel) {
|
var grid = gridModel.coordinateSystem;
|
if (grid === this) {
|
cartesian = this._coordsList[0];
|
}
|
}
|
|
return {cartesian: cartesian, axis: axis};
|
};
|
|
/**
|
* @implements
|
* see {module:echarts/CoodinateSystem}
|
*/
|
gridProto.containPoint = function (point) {
|
var coord = this._coordsList[0];
|
if (coord) {
|
return coord.containPoint(point);
|
}
|
};
|
|
/**
|
* Initialize cartesian coordinate systems
|
* @private
|
*/
|
gridProto._initCartesian = function (gridModel, ecModel, api) {
|
var axisPositionUsed = {
|
left: false,
|
right: false,
|
top: false,
|
bottom: false
|
};
|
|
var axesMap = {
|
x: {},
|
y: {}
|
};
|
var axesCount = {
|
x: 0,
|
y: 0
|
};
|
|
/// Create axis
|
ecModel.eachComponent('xAxis', createAxisCreator('x'), this);
|
ecModel.eachComponent('yAxis', createAxisCreator('y'), this);
|
|
if (!axesCount.x || !axesCount.y) {
|
// Roll back when there no either x or y axis
|
this._axesMap = {};
|
this._axesList = [];
|
return;
|
}
|
|
this._axesMap = axesMap;
|
|
/// Create cartesian2d
|
each(axesMap.x, function (xAxis, xAxisIndex) {
|
each(axesMap.y, function (yAxis, yAxisIndex) {
|
var key = 'x' + xAxisIndex + 'y' + yAxisIndex;
|
var cartesian = new Cartesian2D(key);
|
|
cartesian.grid = this;
|
cartesian.model = gridModel;
|
|
this._coordsMap[key] = cartesian;
|
this._coordsList.push(cartesian);
|
|
cartesian.addAxis(xAxis);
|
cartesian.addAxis(yAxis);
|
}, this);
|
}, this);
|
|
function createAxisCreator(axisType) {
|
return function (axisModel, idx) {
|
if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) {
|
return;
|
}
|
|
var axisPosition = axisModel.get('position');
|
if (axisType === 'x') {
|
// Fix position
|
if (axisPosition !== 'top' && axisPosition !== 'bottom') {
|
// Default bottom of X
|
axisPosition = 'bottom';
|
if (axisPositionUsed[axisPosition]) {
|
axisPosition = axisPosition === 'top' ? 'bottom' : 'top';
|
}
|
}
|
}
|
else {
|
// Fix position
|
if (axisPosition !== 'left' && axisPosition !== 'right') {
|
// Default left of Y
|
axisPosition = 'left';
|
if (axisPositionUsed[axisPosition]) {
|
axisPosition = axisPosition === 'left' ? 'right' : 'left';
|
}
|
}
|
}
|
axisPositionUsed[axisPosition] = true;
|
|
var axis = new Axis2D(
|
axisType, axisHelper.createScaleByModel(axisModel),
|
[0, 0],
|
axisModel.get('type'),
|
axisPosition
|
);
|
|
var isCategory = axis.type === 'category';
|
axis.onBand = isCategory && axisModel.get('boundaryGap');
|
axis.inverse = axisModel.get('inverse');
|
|
axis.onZero = axisModel.get('axisLine.onZero');
|
|
// Inject axis into axisModel
|
axisModel.axis = axis;
|
|
// Inject axisModel into axis
|
axis.model = axisModel;
|
|
// Inject grid info axis
|
axis.grid = this;
|
|
// Index of axis, can be used as key
|
axis.index = idx;
|
|
this._axesList.push(axis);
|
|
axesMap[axisType][idx] = axis;
|
axesCount[axisType]++;
|
};
|
}
|
};
|
|
/**
|
* Update cartesian properties from series
|
* @param {module:echarts/model/Option} option
|
* @private
|
*/
|
gridProto._updateScale = function (ecModel, gridModel) {
|
// Reset scale
|
zrUtil.each(this._axesList, function (axis) {
|
axis.scale.setExtent(Infinity, -Infinity);
|
});
|
ecModel.eachSeries(function (seriesModel) {
|
if (isCartesian2D(seriesModel)) {
|
var axesModels = findAxesModels(seriesModel, ecModel);
|
var xAxisModel = axesModels[0];
|
var yAxisModel = axesModels[1];
|
|
if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel)
|
|| !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel)
|
) {
|
return;
|
}
|
|
var cartesian = this.getCartesian(
|
xAxisModel.componentIndex, yAxisModel.componentIndex
|
);
|
var data = seriesModel.getData();
|
var xAxis = cartesian.getAxis('x');
|
var yAxis = cartesian.getAxis('y');
|
|
if (data.type === 'list') {
|
unionExtent(data, xAxis, seriesModel);
|
unionExtent(data, yAxis, seriesModel);
|
}
|
}
|
}, this);
|
|
function unionExtent(data, axis, seriesModel) {
|
each(seriesModel.coordDimToDataDim(axis.dim), function (dim) {
|
axis.scale.unionExtentFromData(data, dim);
|
});
|
}
|
};
|
|
/**
|
* @param {string} [dim] 'x' or 'y' or 'auto' or null/undefined
|
* @return {Object} {baseAxes: [], otherAxes: []}
|
*/
|
gridProto.getTooltipAxes = function (dim) {
|
var baseAxes = [];
|
var otherAxes = [];
|
|
each(this.getCartesians(), function (cartesian) {
|
var baseAxis = (dim != null && dim !== 'auto')
|
? cartesian.getAxis(dim) : cartesian.getBaseAxis();
|
var otherAxis = cartesian.getOtherAxis(baseAxis);
|
zrUtil.indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis);
|
zrUtil.indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis);
|
});
|
|
return {baseAxes: baseAxes, otherAxes: otherAxes};
|
};
|
|
/**
|
* @inner
|
*/
|
function updateAxisTransfrom(axis, coordBase) {
|
var axisExtent = axis.getExtent();
|
var axisExtentSum = axisExtent[0] + axisExtent[1];
|
|
// Fast transform
|
axis.toGlobalCoord = axis.dim === 'x'
|
? function (coord) {
|
return coord + coordBase;
|
}
|
: function (coord) {
|
return axisExtentSum - coord + coordBase;
|
};
|
axis.toLocalCoord = axis.dim === 'x'
|
? function (coord) {
|
return coord - coordBase;
|
}
|
: function (coord) {
|
return axisExtentSum - coord + coordBase;
|
};
|
}
|
|
var axesTypes = ['xAxis', 'yAxis'];
|
/**
|
* @inner
|
*/
|
function findAxesModels(seriesModel, ecModel) {
|
return zrUtil.map(axesTypes, function (axisType) {
|
var axisModel = seriesModel.getReferringComponents(axisType)[0];
|
|
if (true) {
|
if (!axisModel) {
|
throw new Error(axisType + ' "' + zrUtil.retrieve(
|
seriesModel.get(axisType + 'Index'),
|
seriesModel.get(axisType + 'Id'),
|
0
|
) + '" not found');
|
}
|
}
|
return axisModel;
|
});
|
}
|
|
/**
|
* @inner
|
*/
|
function isCartesian2D(seriesModel) {
|
return seriesModel.get('coordinateSystem') === 'cartesian2d';
|
}
|
|
Grid.create = function (ecModel, api) {
|
var grids = [];
|
ecModel.eachComponent('grid', function (gridModel, idx) {
|
var grid = new Grid(gridModel, ecModel, api);
|
grid.name = 'grid_' + idx;
|
grid.resize(gridModel, api);
|
|
gridModel.coordinateSystem = grid;
|
|
grids.push(grid);
|
});
|
|
// Inject the coordinateSystems into seriesModel
|
ecModel.eachSeries(function (seriesModel) {
|
if (!isCartesian2D(seriesModel)) {
|
return;
|
}
|
|
var axesModels = findAxesModels(seriesModel, ecModel);
|
var xAxisModel = axesModels[0];
|
var yAxisModel = axesModels[1];
|
|
var gridModel = xAxisModel.getCoordSysModel();
|
|
if (true) {
|
if (!gridModel) {
|
throw new Error(
|
'Grid "' + zrUtil.retrieve(
|
xAxisModel.get('gridIndex'),
|
xAxisModel.get('gridId'),
|
0
|
) + '" not found'
|
);
|
}
|
if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) {
|
throw new Error('xAxis and yAxis must use the same grid');
|
}
|
}
|
|
var grid = gridModel.coordinateSystem;
|
|
seriesModel.coordinateSystem = grid.getCartesian(
|
xAxisModel.componentIndex, yAxisModel.componentIndex
|
);
|
});
|
|
return grids;
|
};
|
|
// For deciding which dimensions to use when creating list data
|
Grid.dimensions = Grid.prototype.dimensions = Cartesian2D.prototype.dimensions;
|
|
__webpack_require__(26).register('cartesian2d', Grid);
|
|
module.exports = Grid;
|
|
|
/***/ },
|
/* 125 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var Cartesian = __webpack_require__(126);
|
|
function Cartesian2D(name) {
|
|
Cartesian.call(this, name);
|
}
|
|
Cartesian2D.prototype = {
|
|
constructor: Cartesian2D,
|
|
type: 'cartesian2d',
|
|
/**
|
* @type {Array.<string>}
|
* @readOnly
|
*/
|
dimensions: ['x', 'y'],
|
|
/**
|
* Base axis will be used on stacking.
|
*
|
* @return {module:echarts/coord/cartesian/Axis2D}
|
*/
|
getBaseAxis: function () {
|
return this.getAxesByScale('ordinal')[0]
|
|| this.getAxesByScale('time')[0]
|
|| this.getAxis('x');
|
},
|
|
/**
|
* If contain point
|
* @param {Array.<number>} point
|
* @return {boolean}
|
*/
|
containPoint: function (point) {
|
var axisX = this.getAxis('x');
|
var axisY = this.getAxis('y');
|
return axisX.contain(axisX.toLocalCoord(point[0]))
|
&& axisY.contain(axisY.toLocalCoord(point[1]));
|
},
|
|
/**
|
* If contain data
|
* @param {Array.<number>} data
|
* @return {boolean}
|
*/
|
containData: function (data) {
|
return this.getAxis('x').containData(data[0])
|
&& this.getAxis('y').containData(data[1]);
|
},
|
|
/**
|
* Convert series data to an array of points
|
* @param {module:echarts/data/List} data
|
* @param {boolean} stack
|
* @return {Array}
|
* Return array of points. For example:
|
* `[[10, 10], [20, 20], [30, 30]]`
|
*/
|
dataToPoints: function (data, stack) {
|
return data.mapArray(['x', 'y'], function (x, y) {
|
return this.dataToPoint([x, y]);
|
}, stack, this);
|
},
|
|
/**
|
* @param {Array.<number>} data
|
* @param {boolean} [clamp=false]
|
* @return {Array.<number>}
|
*/
|
dataToPoint: function (data, clamp) {
|
var xAxis = this.getAxis('x');
|
var yAxis = this.getAxis('y');
|
return [
|
xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)),
|
yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp))
|
];
|
},
|
|
/**
|
* @param {Array.<number>} point
|
* @param {boolean} [clamp=false]
|
* @return {Array.<number>}
|
*/
|
pointToData: function (point, clamp) {
|
var xAxis = this.getAxis('x');
|
var yAxis = this.getAxis('y');
|
return [
|
xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp),
|
yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp)
|
];
|
},
|
|
/**
|
* Get other axis
|
* @param {module:echarts/coord/cartesian/Axis2D} axis
|
*/
|
getOtherAxis: function (axis) {
|
return this.getAxis(axis.dim === 'x' ? 'y' : 'x');
|
}
|
};
|
|
zrUtil.inherits(Cartesian2D, Cartesian);
|
|
module.exports = Cartesian2D;
|
|
|
/***/ },
|
/* 126 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Cartesian coordinate system
|
* @module echarts/coord/Cartesian
|
*
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
|
function dimAxisMapper(dim) {
|
return this._axes[dim];
|
}
|
|
/**
|
* @alias module:echarts/coord/Cartesian
|
* @constructor
|
*/
|
var Cartesian = function (name) {
|
this._axes = {};
|
|
this._dimList = [];
|
|
/**
|
* @type {string}
|
*/
|
this.name = name || '';
|
};
|
|
Cartesian.prototype = {
|
|
constructor: Cartesian,
|
|
type: 'cartesian',
|
|
/**
|
* Get axis
|
* @param {number|string} dim
|
* @return {module:echarts/coord/Cartesian~Axis}
|
*/
|
getAxis: function (dim) {
|
return this._axes[dim];
|
},
|
|
/**
|
* Get axes list
|
* @return {Array.<module:echarts/coord/Cartesian~Axis>}
|
*/
|
getAxes: function () {
|
return zrUtil.map(this._dimList, dimAxisMapper, this);
|
},
|
|
/**
|
* Get axes list by given scale type
|
*/
|
getAxesByScale: function (scaleType) {
|
scaleType = scaleType.toLowerCase();
|
return zrUtil.filter(
|
this.getAxes(),
|
function (axis) {
|
return axis.scale.type === scaleType;
|
}
|
);
|
},
|
|
/**
|
* Add axis
|
* @param {module:echarts/coord/Cartesian.Axis}
|
*/
|
addAxis: function (axis) {
|
var dim = axis.dim;
|
|
this._axes[dim] = axis;
|
|
this._dimList.push(dim);
|
},
|
|
/**
|
* Convert data to coord in nd space
|
* @param {Array.<number>|Object.<string, number>} val
|
* @return {Array.<number>|Object.<string, number>}
|
*/
|
dataToCoord: function (val) {
|
return this._dataCoordConvert(val, 'dataToCoord');
|
},
|
|
/**
|
* Convert coord in nd space to data
|
* @param {Array.<number>|Object.<string, number>} val
|
* @return {Array.<number>|Object.<string, number>}
|
*/
|
coordToData: function (val) {
|
return this._dataCoordConvert(val, 'coordToData');
|
},
|
|
_dataCoordConvert: function (input, method) {
|
var dimList = this._dimList;
|
|
var output = input instanceof Array ? [] : {};
|
|
for (var i = 0; i < dimList.length; i++) {
|
var dim = dimList[i];
|
var axis = this._axes[dim];
|
|
output[dim] = axis[method](input[dim]);
|
}
|
|
return output;
|
}
|
};
|
|
module.exports = Cartesian;
|
|
|
/***/ },
|
/* 127 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var Axis = __webpack_require__(100);
|
var axisLabelInterval = __webpack_require__(128);
|
|
/**
|
* Extend axis 2d
|
* @constructor module:echarts/coord/cartesian/Axis2D
|
* @extends {module:echarts/coord/cartesian/Axis}
|
* @param {string} dim
|
* @param {*} scale
|
* @param {Array.<number>} coordExtent
|
* @param {string} axisType
|
* @param {string} position
|
*/
|
var Axis2D = function (dim, scale, coordExtent, axisType, position) {
|
Axis.call(this, dim, scale, coordExtent);
|
/**
|
* Axis type
|
* - 'category'
|
* - 'value'
|
* - 'time'
|
* - 'log'
|
* @type {string}
|
*/
|
this.type = axisType || 'value';
|
|
/**
|
* Axis position
|
* - 'top'
|
* - 'bottom'
|
* - 'left'
|
* - 'right'
|
*/
|
this.position = position || 'bottom';
|
};
|
|
Axis2D.prototype = {
|
|
constructor: Axis2D,
|
|
/**
|
* Index of axis, can be used as key
|
*/
|
index: 0,
|
/**
|
* If axis is on the zero position of the other axis
|
* @type {boolean}
|
*/
|
onZero: false,
|
|
/**
|
* Axis model
|
* @param {module:echarts/coord/cartesian/AxisModel}
|
*/
|
model: null,
|
|
isHorizontal: function () {
|
var position = this.position;
|
return position === 'top' || position === 'bottom';
|
},
|
|
/**
|
* Each item cooresponds to this.getExtent(), which
|
* means globalExtent[0] may greater than globalExtent[1],
|
* unless `asc` is input.
|
*
|
* @param {boolean} [asc]
|
* @return {Array.<number>}
|
*/
|
getGlobalExtent: function (asc) {
|
var ret = this.getExtent();
|
ret[0] = this.toGlobalCoord(ret[0]);
|
ret[1] = this.toGlobalCoord(ret[1]);
|
asc && ret[0] > ret[1] && ret.reverse();
|
return ret;
|
},
|
|
getOtherAxis: function () {
|
this.grid.getOtherAxis();
|
},
|
|
/**
|
* @return {number}
|
*/
|
getLabelInterval: function () {
|
var labelInterval = this._labelInterval;
|
if (!labelInterval) {
|
labelInterval = this._labelInterval = axisLabelInterval(this);
|
}
|
return labelInterval;
|
},
|
|
/**
|
* If label is ignored.
|
* Automatically used when axis is category and label can not be all shown
|
* @param {number} idx
|
* @return {boolean}
|
*/
|
isLabelIgnored: function (idx) {
|
if (this.type === 'category') {
|
var labelInterval = this.getLabelInterval();
|
return ((typeof labelInterval === 'function')
|
&& !labelInterval(idx, this.scale.getLabel(idx)))
|
|| idx % (labelInterval + 1);
|
}
|
},
|
|
/**
|
* @override
|
*/
|
pointToData: function (point, clamp) {
|
return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
|
},
|
|
/**
|
* Transform global coord to local coord,
|
* i.e. var localCoord = axis.toLocalCoord(80);
|
* designate by module:echarts/coord/cartesian/Grid.
|
* @type {Function}
|
*/
|
toLocalCoord: null,
|
|
/**
|
* Transform global coord to local coord,
|
* i.e. var globalCoord = axis.toLocalCoord(40);
|
* designate by module:echarts/coord/cartesian/Grid.
|
* @type {Function}
|
*/
|
toGlobalCoord: null
|
|
};
|
zrUtil.inherits(Axis2D, Axis);
|
|
module.exports = Axis2D;
|
|
|
/***/ },
|
/* 128 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
/**
|
* Helper function for axisLabelInterval calculation
|
*/
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var axisHelper = __webpack_require__(105);
|
|
module.exports = function (axis) {
|
var axisModel = axis.model;
|
var labelModel = axisModel.getModel('axisLabel');
|
var labelInterval = labelModel.get('interval');
|
if (!(axis.type === 'category' && labelInterval === 'auto')) {
|
return labelInterval === 'auto' ? 0 : labelInterval;
|
}
|
|
return axisHelper.getAxisLabelInterval(
|
zrUtil.map(axis.scale.getTicks(), axis.dataToCoord, axis),
|
axisModel.getFormattedLabels(),
|
labelModel.getModel('textStyle').getFont(),
|
axis.isHorizontal()
|
);
|
};
|
|
|
/***/ },
|
/* 129 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// Grid 是在有直角坐标系的时候必须要存在的
|
// 所以这里也要被 Cartesian2D 依赖
|
|
|
__webpack_require__(130);
|
|
var ComponentModel = __webpack_require__(19);
|
|
module.exports = ComponentModel.extend({
|
|
type: 'grid',
|
|
dependencies: ['xAxis', 'yAxis'],
|
|
layoutMode: 'box',
|
|
/**
|
* @type {module:echarts/coord/cartesian/Grid}
|
*/
|
coordinateSystem: null,
|
|
defaultOption: {
|
show: false,
|
zlevel: 0,
|
z: 0,
|
left: '10%',
|
top: 60,
|
right: '10%',
|
bottom: 60,
|
// If grid size contain label
|
containLabel: false,
|
// width: {totalWidth} - left - right,
|
// height: {totalHeight} - top - bottom,
|
backgroundColor: 'rgba(0,0,0,0)',
|
borderWidth: 1,
|
borderColor: '#ccc'
|
}
|
});
|
|
|
/***/ },
|
/* 130 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var ComponentModel = __webpack_require__(19);
|
var zrUtil = __webpack_require__(4);
|
var axisModelCreator = __webpack_require__(131);
|
|
var AxisModel = ComponentModel.extend({
|
|
type: 'cartesian2dAxis',
|
|
/**
|
* @type {module:echarts/coord/cartesian/Axis2D}
|
*/
|
axis: null,
|
|
/**
|
* @override
|
*/
|
init: function () {
|
AxisModel.superApply(this, 'init', arguments);
|
this.resetRange();
|
},
|
|
/**
|
* @override
|
*/
|
mergeOption: function () {
|
AxisModel.superApply(this, 'mergeOption', arguments);
|
this.resetRange();
|
},
|
|
/**
|
* @override
|
*/
|
restoreData: function () {
|
AxisModel.superApply(this, 'restoreData', arguments);
|
this.resetRange();
|
},
|
|
/**
|
* @override
|
* @return {module:echarts/model/Component}
|
*/
|
getCoordSysModel: function () {
|
return this.ecModel.queryComponents({
|
mainType: 'grid',
|
index: this.option.gridIndex,
|
id: this.option.gridId
|
})[0];
|
}
|
|
});
|
|
function getAxisType(axisDim, option) {
|
// Default axis with data is category axis
|
return option.type || (option.data ? 'category' : 'value');
|
}
|
|
zrUtil.merge(AxisModel.prototype, __webpack_require__(112));
|
|
var extraOption = {
|
// gridIndex: 0,
|
// gridId: '',
|
|
// Offset is for multiple axis on the same position
|
offset: 0
|
};
|
|
axisModelCreator('x', AxisModel, getAxisType, extraOption);
|
axisModelCreator('y', AxisModel, getAxisType, extraOption);
|
|
module.exports = AxisModel;
|
|
|
/***/ },
|
/* 131 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var axisDefault = __webpack_require__(132);
|
var zrUtil = __webpack_require__(4);
|
var ComponentModel = __webpack_require__(19);
|
var layout = __webpack_require__(21);
|
|
// FIXME axisType is fixed ?
|
var AXIS_TYPES = ['value', 'category', 'time', 'log'];
|
|
/**
|
* Generate sub axis model class
|
* @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel'
|
* @param {module:echarts/model/Component} BaseAxisModelClass
|
* @param {Function} axisTypeDefaulter
|
* @param {Object} [extraDefaultOption]
|
*/
|
module.exports = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) {
|
|
zrUtil.each(AXIS_TYPES, function (axisType) {
|
|
BaseAxisModelClass.extend({
|
|
type: axisName + 'Axis.' + axisType,
|
|
mergeDefaultAndTheme: function (option, ecModel) {
|
var layoutMode = this.layoutMode;
|
var inputPositionParams = layoutMode
|
? layout.getLayoutParams(option) : {};
|
|
var themeModel = ecModel.getTheme();
|
zrUtil.merge(option, themeModel.get(axisType + 'Axis'));
|
zrUtil.merge(option, this.getDefaultOption());
|
|
option.type = axisTypeDefaulter(axisName, option);
|
|
if (layoutMode) {
|
layout.mergeLayoutParam(option, inputPositionParams, layoutMode);
|
}
|
},
|
|
defaultOption: zrUtil.mergeAll(
|
[
|
{},
|
axisDefault[axisType + 'Axis'],
|
extraDefaultOption
|
],
|
true
|
)
|
});
|
});
|
|
ComponentModel.registerSubTypeDefaulter(
|
axisName + 'Axis',
|
zrUtil.curry(axisTypeDefaulter, axisName)
|
);
|
};
|
|
|
/***/ },
|
/* 132 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var defaultOption = {
|
show: true,
|
zlevel: 0, // 一级层叠
|
z: 0, // 二级层叠
|
// 反向坐标轴
|
inverse: false,
|
|
// 坐标轴名字,默认为空
|
name: '',
|
// 坐标轴名字位置,支持'start' | 'middle' | 'end'
|
nameLocation: 'end',
|
// 坐标轴名字旋转,degree。
|
nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'.
|
nameTruncate: {
|
maxWidth: null,
|
ellipsis: '...',
|
placeholder: '.'
|
},
|
// 坐标轴文字样式,默认取全局样式
|
nameTextStyle: {},
|
// 文字与轴线距离
|
nameGap: 15,
|
|
silent: false, // Default false to support tooltip.
|
triggerEvent: false, // Default false to avoid legacy user event listener fail.
|
|
tooltip: {
|
show: false
|
},
|
|
axisPointer: {},
|
|
// 坐标轴线
|
axisLine: {
|
// 默认显示,属性show控制显示与否
|
show: true,
|
onZero: true,
|
// 属性lineStyle控制线条样式
|
lineStyle: {
|
color: '#333',
|
width: 1,
|
type: 'solid'
|
}
|
},
|
// 坐标轴小标记
|
axisTick: {
|
// 属性show控制显示与否,默认显示
|
show: true,
|
// 控制小标记是否在grid里
|
inside: false,
|
// 属性length控制线长
|
length: 5,
|
// 属性lineStyle控制线条样式
|
lineStyle: {
|
width: 1
|
}
|
},
|
// 坐标轴文本标签,详见axis.axisLabel
|
axisLabel: {
|
show: true,
|
// 控制文本标签是否在grid里
|
inside: false,
|
rotate: 0,
|
showMinLabel: null, // true | false | null (auto)
|
showMaxLabel: null, // true | false | null (auto)
|
margin: 8,
|
// formatter: null,
|
// 其余属性默认使用全局文本样式,详见TEXTSTYLE
|
textStyle: {
|
fontSize: 12
|
}
|
},
|
// 分隔线
|
splitLine: {
|
// 默认显示,属性show控制显示与否
|
show: true,
|
// 属性lineStyle(详见lineStyle)控制线条样式
|
lineStyle: {
|
color: ['#ccc'],
|
width: 1,
|
type: 'solid'
|
}
|
},
|
// 分隔区域
|
splitArea: {
|
// 默认不显示,属性show控制显示与否
|
show: false,
|
// 属性areaStyle(详见areaStyle)控制区域样式
|
areaStyle: {
|
color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)']
|
}
|
}
|
};
|
|
var categoryAxis = zrUtil.merge({
|
// 类目起始和结束两端空白策略
|
boundaryGap: true,
|
// splitArea: {
|
// show: false
|
// },
|
splitLine: {
|
show: false
|
},
|
// 坐标轴小标记
|
axisTick: {
|
// If tick is align with label when boundaryGap is true
|
alignWithLabel: false,
|
interval: 'auto'
|
},
|
// 坐标轴文本标签,详见axis.axisLabel
|
axisLabel: {
|
interval: 'auto'
|
}
|
}, defaultOption);
|
|
var valueAxis = zrUtil.merge({
|
// 数值起始和结束两端空白策略
|
boundaryGap: [0, 0],
|
// 最小值, 设置成 'dataMin' 则从数据中计算最小值
|
// min: null,
|
// 最大值,设置成 'dataMax' 则从数据中计算最大值
|
// max: null,
|
// Readonly prop, specifies start value of the range when using data zoom.
|
// rangeStart: null
|
// Readonly prop, specifies end value of the range when using data zoom.
|
// rangeEnd: null
|
// 脱离0值比例,放大聚焦到最终_min,_max区间
|
// scale: false,
|
// 分割段数,默认为5
|
splitNumber: 5
|
// Minimum interval
|
// minInterval: null
|
}, defaultOption);
|
|
// FIXME
|
var timeAxis = zrUtil.defaults({
|
scale: true,
|
min: 'dataMin',
|
max: 'dataMax'
|
}, valueAxis);
|
var logAxis = zrUtil.defaults({
|
logBase: 10
|
}, valueAxis);
|
logAxis.scale = true;
|
|
module.exports = {
|
categoryAxis: categoryAxis,
|
valueAxis: valueAxis,
|
timeAxis: timeAxis,
|
logAxis: logAxis
|
};
|
|
|
/***/ },
|
/* 133 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// TODO boundaryGap
|
|
|
__webpack_require__(130);
|
|
__webpack_require__(134);
|
|
|
/***/ },
|
/* 134 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var AxisBuilder = __webpack_require__(135);
|
var AxisView = __webpack_require__(136);
|
var cartesianAxisHelper = __webpack_require__(138);
|
var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick;
|
var getInterval = AxisBuilder.getInterval;
|
|
var axisBuilderAttrs = [
|
'axisLine', 'axisLabel', 'axisTick', 'axisName'
|
];
|
var selfBuilderAttrs = [
|
'splitArea', 'splitLine'
|
];
|
|
// function getAlignWithLabel(model, axisModel) {
|
// var alignWithLabel = model.get('alignWithLabel');
|
// if (alignWithLabel === 'auto') {
|
// alignWithLabel = axisModel.get('axisTick.alignWithLabel');
|
// }
|
// return alignWithLabel;
|
// }
|
|
var CartesianAxisView = AxisView.extend({
|
|
type: 'cartesianAxis',
|
|
axisPointerClass: 'CartesianAxisPointer',
|
|
/**
|
* @override
|
*/
|
render: function (axisModel, ecModel, api, payload) {
|
|
this.group.removeAll();
|
|
var oldAxisGroup = this._axisGroup;
|
this._axisGroup = new graphic.Group();
|
|
this.group.add(this._axisGroup);
|
|
if (!axisModel.get('show')) {
|
return;
|
}
|
|
var gridModel = axisModel.getCoordSysModel();
|
|
var layout = cartesianAxisHelper.layout(gridModel, axisModel);
|
|
var axisBuilder = new AxisBuilder(axisModel, layout);
|
|
zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder);
|
|
this._axisGroup.add(axisBuilder.getGroup());
|
|
zrUtil.each(selfBuilderAttrs, function (name) {
|
if (axisModel.get(name + '.show')) {
|
this['_' + name](axisModel, gridModel, layout.labelInterval);
|
}
|
}, this);
|
|
graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel);
|
|
CartesianAxisView.superCall(this, 'render', axisModel, ecModel, api, payload);
|
},
|
|
/**
|
* @param {module:echarts/coord/cartesian/AxisModel} axisModel
|
* @param {module:echarts/coord/cartesian/GridModel} gridModel
|
* @param {number|Function} labelInterval
|
* @private
|
*/
|
_splitLine: function (axisModel, gridModel, labelInterval) {
|
var axis = axisModel.axis;
|
|
if (axis.scale.isBlank()) {
|
return;
|
}
|
|
var splitLineModel = axisModel.getModel('splitLine');
|
var lineStyleModel = splitLineModel.getModel('lineStyle');
|
var lineColors = lineStyleModel.get('color');
|
|
var lineInterval = getInterval(splitLineModel, labelInterval);
|
|
lineColors = zrUtil.isArray(lineColors) ? lineColors : [lineColors];
|
|
var gridRect = gridModel.coordinateSystem.getRect();
|
var isHorizontal = axis.isHorizontal();
|
|
var lineCount = 0;
|
|
var ticksCoords = axis.getTicksCoords(
|
// splitLineModel.get('alignWithLabel')
|
);
|
var ticks = axis.scale.getTicks();
|
|
var p1 = [];
|
var p2 = [];
|
// Simple optimization
|
// Batching the lines if color are the same
|
var lineStyle = lineStyleModel.getLineStyle();
|
for (var i = 0; i < ticksCoords.length; i++) {
|
if (ifIgnoreOnTick(axis, i, lineInterval)) {
|
continue;
|
}
|
|
var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
|
|
if (isHorizontal) {
|
p1[0] = tickCoord;
|
p1[1] = gridRect.y;
|
p2[0] = tickCoord;
|
p2[1] = gridRect.y + gridRect.height;
|
}
|
else {
|
p1[0] = gridRect.x;
|
p1[1] = tickCoord;
|
p2[0] = gridRect.x + gridRect.width;
|
p2[1] = tickCoord;
|
}
|
|
var colorIndex = (lineCount++) % lineColors.length;
|
this._axisGroup.add(new graphic.Line(graphic.subPixelOptimizeLine({
|
anid: 'line_' + ticks[i],
|
|
shape: {
|
x1: p1[0],
|
y1: p1[1],
|
x2: p2[0],
|
y2: p2[1]
|
},
|
style: zrUtil.defaults({
|
stroke: lineColors[colorIndex]
|
}, lineStyle),
|
silent: true
|
})));
|
}
|
},
|
|
/**
|
* @param {module:echarts/coord/cartesian/AxisModel} axisModel
|
* @param {module:echarts/coord/cartesian/GridModel} gridModel
|
* @param {number|Function} labelInterval
|
* @private
|
*/
|
_splitArea: function (axisModel, gridModel, labelInterval) {
|
var axis = axisModel.axis;
|
|
if (axis.scale.isBlank()) {
|
return;
|
}
|
|
var splitAreaModel = axisModel.getModel('splitArea');
|
var areaStyleModel = splitAreaModel.getModel('areaStyle');
|
var areaColors = areaStyleModel.get('color');
|
|
var gridRect = gridModel.coordinateSystem.getRect();
|
|
var ticksCoords = axis.getTicksCoords(
|
// splitAreaModel.get('alignWithLabel')
|
);
|
var ticks = axis.scale.getTicks();
|
|
var prevX = axis.toGlobalCoord(ticksCoords[0]);
|
var prevY = axis.toGlobalCoord(ticksCoords[0]);
|
|
var count = 0;
|
|
var areaInterval = getInterval(splitAreaModel, labelInterval);
|
|
var areaStyle = areaStyleModel.getAreaStyle();
|
areaColors = zrUtil.isArray(areaColors) ? areaColors : [areaColors];
|
|
for (var i = 1; i < ticksCoords.length; i++) {
|
if (ifIgnoreOnTick(axis, i, areaInterval)) {
|
continue;
|
}
|
|
var tickCoord = axis.toGlobalCoord(ticksCoords[i]);
|
|
var x;
|
var y;
|
var width;
|
var height;
|
if (axis.isHorizontal()) {
|
x = prevX;
|
y = gridRect.y;
|
width = tickCoord - x;
|
height = gridRect.height;
|
}
|
else {
|
x = gridRect.x;
|
y = prevY;
|
width = gridRect.width;
|
height = tickCoord - y;
|
}
|
|
var colorIndex = (count++) % areaColors.length;
|
this._axisGroup.add(new graphic.Rect({
|
anid: 'area_' + ticks[i],
|
|
shape: {
|
x: x,
|
y: y,
|
width: width,
|
height: height
|
},
|
style: zrUtil.defaults({
|
fill: areaColors[colorIndex]
|
}, areaStyle),
|
silent: true
|
}));
|
|
prevX = x + width;
|
prevY = y + height;
|
}
|
}
|
});
|
|
CartesianAxisView.extend({
|
type: 'xAxis'
|
});
|
CartesianAxisView.extend({
|
type: 'yAxis'
|
});
|
|
|
|
/***/ },
|
/* 135 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var formatUtil = __webpack_require__(6);
|
var graphic = __webpack_require__(44);
|
var Model = __webpack_require__(12);
|
var numberUtil = __webpack_require__(7);
|
var remRadian = numberUtil.remRadian;
|
var isRadianAroundZero = numberUtil.isRadianAroundZero;
|
var vec2 = __webpack_require__(10);
|
var matrix = __webpack_require__(11);
|
var v2ApplyTransform = vec2.applyTransform;
|
var retrieve = zrUtil.retrieve;
|
|
var PI = Math.PI;
|
|
function makeAxisEventDataBase(axisModel) {
|
var eventData = {
|
componentType: axisModel.mainType
|
};
|
eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex;
|
return eventData;
|
}
|
|
/**
|
* A final axis is translated and rotated from a "standard axis".
|
* So opt.position and opt.rotation is required.
|
*
|
* A standard axis is and axis from [0, 0] to [0, axisExtent[1]],
|
* for example: (0, 0) ------------> (0, 50)
|
*
|
* nameDirection or tickDirection or labelDirection is 1 means tick
|
* or label is below the standard axis, whereas is -1 means above
|
* the standard axis. labelOffset means offset between label and axis,
|
* which is useful when 'onZero', where axisLabel is in the grid and
|
* label in outside grid.
|
*
|
* Tips: like always,
|
* positive rotation represents anticlockwise, and negative rotation
|
* represents clockwise.
|
* The direction of position coordinate is the same as the direction
|
* of screen coordinate.
|
*
|
* Do not need to consider axis 'inverse', which is auto processed by
|
* axis extent.
|
*
|
* @param {module:zrender/container/Group} group
|
* @param {Object} axisModel
|
* @param {Object} opt Standard axis parameters.
|
* @param {Array.<number>} opt.position [x, y]
|
* @param {number} opt.rotation by radian
|
* @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle'.
|
* @param {number} [opt.tickDirection=1] 1 or -1
|
* @param {number} [opt.labelDirection=1] 1 or -1
|
* @param {number} [opt.labelOffset=0] Usefull when onZero.
|
* @param {string} [opt.axisLabelShow] default get from axisModel.
|
* @param {string} [opt.axisName] default get from axisModel.
|
* @param {number} [opt.axisNameAvailableWidth]
|
* @param {number} [opt.labelRotate] by degree, default get from axisModel.
|
* @param {number} [opt.labelInterval] Default label interval when label
|
* interval from model is null or 'auto'.
|
* @param {number} [opt.strokeContainThreshold] Default label interval when label
|
* @param {number} [opt.nameTruncateMaxWidth]
|
*/
|
var AxisBuilder = function (axisModel, opt) {
|
|
/**
|
* @readOnly
|
*/
|
this.opt = opt;
|
|
/**
|
* @readOnly
|
*/
|
this.axisModel = axisModel;
|
|
// Default value
|
zrUtil.defaults(
|
opt,
|
{
|
labelOffset: 0,
|
nameDirection: 1,
|
tickDirection: 1,
|
labelDirection: 1,
|
silent: true
|
}
|
);
|
|
/**
|
* @readOnly
|
*/
|
this.group = new graphic.Group();
|
|
// FIXME Not use a seperate text group?
|
var dumbGroup = new graphic.Group({
|
position: opt.position.slice(),
|
rotation: opt.rotation
|
});
|
|
// this.group.add(dumbGroup);
|
// this._dumbGroup = dumbGroup;
|
|
dumbGroup.updateTransform();
|
this._transform = dumbGroup.transform;
|
|
this._dumbGroup = dumbGroup;
|
};
|
|
AxisBuilder.prototype = {
|
|
constructor: AxisBuilder,
|
|
hasBuilder: function (name) {
|
return !!builders[name];
|
},
|
|
add: function (name) {
|
builders[name].call(this);
|
},
|
|
getGroup: function () {
|
return this.group;
|
}
|
|
};
|
|
var builders = {
|
|
/**
|
* @private
|
*/
|
axisLine: function () {
|
var opt = this.opt;
|
var axisModel = this.axisModel;
|
|
if (!axisModel.get('axisLine.show')) {
|
return;
|
}
|
|
var extent = this.axisModel.axis.getExtent();
|
|
var matrix = this._transform;
|
var pt1 = [extent[0], 0];
|
var pt2 = [extent[1], 0];
|
if (matrix) {
|
v2ApplyTransform(pt1, pt1, matrix);
|
v2ApplyTransform(pt2, pt2, matrix);
|
}
|
|
this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({
|
|
// Id for animation
|
anid: 'line',
|
|
shape: {
|
x1: pt1[0],
|
y1: pt1[1],
|
x2: pt2[0],
|
y2: pt2[1]
|
},
|
style: zrUtil.extend(
|
{lineCap: 'round'},
|
axisModel.getModel('axisLine.lineStyle').getLineStyle()
|
),
|
strokeContainThreshold: opt.strokeContainThreshold || 5,
|
silent: true,
|
z2: 1
|
})));
|
},
|
|
/**
|
* @private
|
*/
|
axisTick: function () {
|
var axisModel = this.axisModel;
|
var axis = axisModel.axis;
|
|
if (!axisModel.get('axisTick.show') || axis.scale.isBlank()) {
|
return;
|
}
|
|
var tickModel = axisModel.getModel('axisTick');
|
var opt = this.opt;
|
|
var lineStyleModel = tickModel.getModel('lineStyle');
|
var tickLen = tickModel.get('length');
|
|
var tickInterval = getInterval(tickModel, opt.labelInterval);
|
var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel'));
|
var ticks = axis.scale.getTicks();
|
|
var pt1 = [];
|
var pt2 = [];
|
var matrix = this._transform;
|
|
for (var i = 0; i < ticksCoords.length; i++) {
|
// Only ordinal scale support tick interval
|
if (ifIgnoreOnTick(axis, i, tickInterval)) {
|
continue;
|
}
|
|
var tickCoord = ticksCoords[i];
|
|
pt1[0] = tickCoord;
|
pt1[1] = 0;
|
pt2[0] = tickCoord;
|
pt2[1] = opt.tickDirection * tickLen;
|
|
if (matrix) {
|
v2ApplyTransform(pt1, pt1, matrix);
|
v2ApplyTransform(pt2, pt2, matrix);
|
}
|
// Tick line, Not use group transform to have better line draw
|
this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({
|
|
// Id for animation
|
anid: 'tick_' + ticks[i],
|
|
shape: {
|
x1: pt1[0],
|
y1: pt1[1],
|
x2: pt2[0],
|
y2: pt2[1]
|
},
|
style: zrUtil.defaults(
|
lineStyleModel.getLineStyle(),
|
{
|
stroke: axisModel.get('axisLine.lineStyle.color')
|
}
|
),
|
z2: 2,
|
silent: true
|
})));
|
}
|
},
|
|
/**
|
* @param {module:echarts/coord/cartesian/AxisModel} axisModel
|
* @param {module:echarts/coord/cartesian/GridModel} gridModel
|
* @private
|
*/
|
axisLabel: function () {
|
var opt = this.opt;
|
var axisModel = this.axisModel;
|
var axis = axisModel.axis;
|
var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show'));
|
|
if (!show || axis.scale.isBlank()) {
|
return;
|
}
|
|
var labelModel = axisModel.getModel('axisLabel');
|
var textStyleModel = labelModel.getModel('textStyle');
|
var labelMargin = labelModel.get('margin');
|
var ticks = axis.scale.getTicks();
|
var labels = axisModel.getFormattedLabels();
|
|
// Special label rotate.
|
var labelRotation = (
|
retrieve(opt.labelRotate, labelModel.get('rotate')) || 0
|
) * PI / 180;
|
|
var labelLayout = innerTextLayout(opt.rotation, labelRotation, opt.labelDirection);
|
var categoryData = axisModel.get('data');
|
|
var textEls = [];
|
var silent = isSilent(axisModel);
|
var triggerEvent = axisModel.get('triggerEvent');
|
|
zrUtil.each(ticks, function (tickVal, index) {
|
if (ifIgnoreOnTick(axis, index, opt.labelInterval)) {
|
return;
|
}
|
|
var itemTextStyleModel = textStyleModel;
|
if (categoryData && categoryData[tickVal] && categoryData[tickVal].textStyle) {
|
itemTextStyleModel = new Model(
|
categoryData[tickVal].textStyle, textStyleModel, axisModel.ecModel
|
);
|
}
|
var textColor = itemTextStyleModel.getTextColor()
|
|| axisModel.get('axisLine.lineStyle.color');
|
|
var tickCoord = axis.dataToCoord(tickVal);
|
var pos = [
|
tickCoord,
|
opt.labelOffset + opt.labelDirection * labelMargin
|
];
|
var labelStr = axis.scale.getLabel(tickVal);
|
|
var textEl = new graphic.Text({
|
|
// Id for animation
|
anid: 'label_' + tickVal,
|
|
style: {
|
text: labels[index],
|
textAlign: itemTextStyleModel.get('align', true) || labelLayout.textAlign,
|
textVerticalAlign: itemTextStyleModel.get('baseline', true) || labelLayout.textVerticalAlign,
|
textFont: itemTextStyleModel.getFont(),
|
fill: typeof textColor === 'function'
|
? textColor(
|
// (1) In category axis with data zoom, tick is not the original
|
// index of axis.data. So tick should not be exposed to user
|
// in category axis.
|
// (2) Compatible with previous version, which always returns labelStr.
|
// But in interval scale labelStr is like '223,445', which maked
|
// user repalce ','. So we modify it to return original val but remain
|
// it as 'string' to avoid error in replacing.
|
axis.type === 'category' ? labelStr : axis.type === 'value' ? tickVal + '' : tickVal,
|
index
|
)
|
: textColor
|
},
|
position: pos,
|
rotation: labelLayout.rotation,
|
silent: silent,
|
z2: 10
|
});
|
|
// Pack data for mouse event
|
if (triggerEvent) {
|
textEl.eventData = makeAxisEventDataBase(axisModel);
|
textEl.eventData.targetType = 'axisLabel';
|
textEl.eventData.value = labelStr;
|
}
|
|
// FIXME
|
this._dumbGroup.add(textEl);
|
textEl.updateTransform();
|
|
textEls.push(textEl);
|
this.group.add(textEl);
|
|
textEl.decomposeTransform();
|
|
}, this);
|
|
fixMinMaxLabelShow(axisModel, textEls);
|
},
|
|
/**
|
* @private
|
*/
|
axisName: function () {
|
var opt = this.opt;
|
var axisModel = this.axisModel;
|
var name = retrieve(opt.axisName, axisModel.get('name'));
|
|
if (!name) {
|
return;
|
}
|
|
var nameLocation = axisModel.get('nameLocation');
|
var nameDirection = opt.nameDirection;
|
var textStyleModel = axisModel.getModel('nameTextStyle');
|
var gap = axisModel.get('nameGap') || 0;
|
|
var extent = this.axisModel.axis.getExtent();
|
var gapSignal = extent[0] > extent[1] ? -1 : 1;
|
var pos = [
|
nameLocation === 'start'
|
? extent[0] - gapSignal * gap
|
: nameLocation === 'end'
|
? extent[1] + gapSignal * gap
|
: (extent[0] + extent[1]) / 2, // 'middle'
|
// Reuse labelOffset.
|
nameLocation === 'middle' ? opt.labelOffset + nameDirection * gap : 0
|
];
|
|
var labelLayout;
|
|
var nameRotation = axisModel.get('nameRotate');
|
if (nameRotation != null) {
|
nameRotation = nameRotation * PI / 180; // To radian.
|
}
|
|
var axisNameAvailableWidth;
|
|
if (nameLocation === 'middle') {
|
labelLayout = innerTextLayout(
|
opt.rotation,
|
nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis.
|
nameDirection
|
);
|
}
|
else {
|
labelLayout = endTextLayout(
|
opt, nameLocation, nameRotation || 0, extent
|
);
|
|
axisNameAvailableWidth = opt.axisNameAvailableWidth;
|
if (axisNameAvailableWidth != null) {
|
axisNameAvailableWidth = Math.abs(
|
axisNameAvailableWidth / Math.sin(labelLayout.rotation)
|
);
|
!isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null);
|
}
|
}
|
|
var textFont = textStyleModel.getFont();
|
|
var truncateOpt = axisModel.get('nameTruncate', true) || {};
|
var ellipsis = truncateOpt.ellipsis;
|
var maxWidth = retrieve(
|
opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth
|
);
|
var truncatedText = (ellipsis != null && maxWidth != null)
|
? formatUtil.truncateText(
|
name, maxWidth, textFont, ellipsis,
|
{minChar: 2, placeholder: truncateOpt.placeholder}
|
)
|
: name;
|
|
var tooltipOpt = axisModel.get('tooltip', true);
|
|
var mainType = axisModel.mainType;
|
var formatterParams = {
|
componentType: mainType,
|
name: name,
|
$vars: ['name']
|
};
|
formatterParams[mainType + 'Index'] = axisModel.componentIndex;
|
|
var textEl = new graphic.Text({
|
|
// Id for animation
|
anid: 'name',
|
|
__fullText: name,
|
__truncatedText: truncatedText,
|
|
style: {
|
text: truncatedText,
|
textFont: textFont,
|
fill: textStyleModel.getTextColor()
|
|| axisModel.get('axisLine.lineStyle.color'),
|
textAlign: labelLayout.textAlign,
|
textVerticalAlign: labelLayout.textVerticalAlign
|
},
|
position: pos,
|
rotation: labelLayout.rotation,
|
silent: isSilent(axisModel),
|
z2: 1,
|
tooltip: (tooltipOpt && tooltipOpt.show)
|
? zrUtil.extend({
|
content: name,
|
formatter: function () {
|
return name;
|
},
|
formatterParams: formatterParams
|
}, tooltipOpt)
|
: null
|
});
|
|
if (axisModel.get('triggerEvent')) {
|
textEl.eventData = makeAxisEventDataBase(axisModel);
|
textEl.eventData.targetType = 'axisName';
|
textEl.eventData.name = name;
|
}
|
|
// FIXME
|
this._dumbGroup.add(textEl);
|
textEl.updateTransform();
|
|
this.group.add(textEl);
|
|
textEl.decomposeTransform();
|
}
|
|
};
|
|
/**
|
* @public
|
* @static
|
* @param {Object} opt
|
* @param {number} axisRotation in radian
|
* @param {number} textRotation in radian
|
* @param {number} direction
|
* @return {Object} {
|
* rotation, // according to axis
|
* textAlign,
|
* textVerticalAlign
|
* }
|
*/
|
var innerTextLayout = AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) {
|
var rotationDiff = remRadian(textRotation - axisRotation);
|
var textAlign;
|
var textVerticalAlign;
|
|
if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line.
|
textVerticalAlign = direction > 0 ? 'top' : 'bottom';
|
textAlign = 'center';
|
}
|
else if (isRadianAroundZero(rotationDiff - PI)) { // Label is inverse parallel with axis line.
|
textVerticalAlign = direction > 0 ? 'bottom' : 'top';
|
textAlign = 'center';
|
}
|
else {
|
textVerticalAlign = 'middle';
|
|
if (rotationDiff > 0 && rotationDiff < PI) {
|
textAlign = direction > 0 ? 'right' : 'left';
|
}
|
else {
|
textAlign = direction > 0 ? 'left' : 'right';
|
}
|
}
|
|
return {
|
rotation: rotationDiff,
|
textAlign: textAlign,
|
textVerticalAlign: textVerticalAlign
|
};
|
};
|
|
function endTextLayout(opt, textPosition, textRotate, extent) {
|
var rotationDiff = remRadian(textRotate - opt.rotation);
|
var textAlign;
|
var textVerticalAlign;
|
var inverse = extent[0] > extent[1];
|
var onLeft = (textPosition === 'start' && !inverse)
|
|| (textPosition !== 'start' && inverse);
|
|
if (isRadianAroundZero(rotationDiff - PI / 2)) {
|
textVerticalAlign = onLeft ? 'bottom' : 'top';
|
textAlign = 'center';
|
}
|
else if (isRadianAroundZero(rotationDiff - PI * 1.5)) {
|
textVerticalAlign = onLeft ? 'top' : 'bottom';
|
textAlign = 'center';
|
}
|
else {
|
textVerticalAlign = 'middle';
|
if (rotationDiff < PI * 1.5 && rotationDiff > PI / 2) {
|
textAlign = onLeft ? 'left' : 'right';
|
}
|
else {
|
textAlign = onLeft ? 'right' : 'left';
|
}
|
}
|
|
return {
|
rotation: rotationDiff,
|
textAlign: textAlign,
|
textVerticalAlign: textVerticalAlign
|
};
|
}
|
|
function isSilent(axisModel) {
|
var tooltipOpt = axisModel.get('tooltip');
|
return axisModel.get('silent')
|
// Consider mouse cursor, add these restrictions.
|
|| !(
|
axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show)
|
);
|
}
|
|
function fixMinMaxLabelShow(axisModel, textEls) {
|
// If min or max are user set, we need to check
|
// If the tick on min(max) are overlap on their neighbour tick
|
// If they are overlapped, we need to hide the min(max) tick label
|
var showMinLabel = axisModel.get('axisLabel.showMinLabel');
|
var showMaxLabel = axisModel.get('axisLabel.showMaxLabel');
|
var firstLabel = textEls[0];
|
var nextLabel = textEls[1];
|
var lastLabel = textEls[textEls.length - 1];
|
var prevLabel = textEls[textEls.length - 2];
|
|
if (showMinLabel === false) {
|
firstLabel.ignore = true;
|
}
|
else if (axisModel.getMin() != null && isTwoLabelOverlapped(firstLabel, nextLabel)) {
|
showMinLabel ? (nextLabel.ignore = true) : (firstLabel.ignore = true);
|
}
|
|
if (showMaxLabel === false) {
|
lastLabel.ignore = true;
|
}
|
else if (axisModel.getMax() != null && isTwoLabelOverlapped(prevLabel, lastLabel)) {
|
showMaxLabel ? (prevLabel.ignore = true) : (lastLabel.ignore = true);
|
}
|
}
|
|
function isTwoLabelOverlapped(current, next, labelLayout) {
|
// current and next has the same rotation.
|
var firstRect = current && current.getBoundingRect().clone();
|
var nextRect = next && next.getBoundingRect().clone();
|
|
if (!firstRect || !nextRect) {
|
return;
|
}
|
|
// When checking intersect of two rotated labels, we use mRotationBack
|
// to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`.
|
var mRotationBack = matrix.identity([]);
|
matrix.rotate(mRotationBack, mRotationBack, -current.rotation);
|
|
firstRect.applyTransform(matrix.mul([], mRotationBack, current.getLocalTransform()));
|
nextRect.applyTransform(matrix.mul([], mRotationBack, next.getLocalTransform()));
|
|
return firstRect.intersect(nextRect);
|
}
|
|
|
/**
|
* @static
|
*/
|
var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick = function (axis, i, interval) {
|
var rawTick;
|
var scale = axis.scale;
|
return scale.type === 'ordinal'
|
&& (
|
typeof interval === 'function'
|
? (
|
rawTick = scale.getTicks()[i],
|
!interval(rawTick, scale.getLabel(rawTick))
|
)
|
: i % (interval + 1)
|
);
|
};
|
|
/**
|
* @static
|
*/
|
var getInterval = AxisBuilder.getInterval = function (model, labelInterval) {
|
var interval = model.get('interval');
|
if (interval == null || interval == 'auto') {
|
interval = labelInterval;
|
}
|
return interval;
|
};
|
|
module.exports = AxisBuilder;
|
|
|
|
/***/ },
|
/* 136 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var axisPointerModelHelper = __webpack_require__(137);
|
|
/**
|
* Base class of AxisView.
|
*/
|
var AxisView = __webpack_require__(1).extendComponentView({
|
|
type: 'axis',
|
|
/**
|
* @private
|
*/
|
_axisPointer: null,
|
|
/**
|
* @protected
|
* @type {string}
|
*/
|
axisPointerClass: null,
|
|
/**
|
* @override
|
*/
|
render: function (axisModel, ecModel, api, payload) {
|
// FIXME
|
// This process should proformed after coordinate systems updated
|
// (axis scale updated), and should be performed each time update.
|
// So put it here temporarily, although it is not appropriate to
|
// put a model-writing procedure in `view`.
|
this.axisPointerClass && axisPointerModelHelper.fixValue(axisModel);
|
|
AxisView.superApply(this, 'render', arguments);
|
|
updateAxisPointer(this, axisModel, ecModel, api, payload, true);
|
},
|
|
/**
|
* Action handler.
|
* @public
|
* @param {module:echarts/coord/cartesian/AxisModel} axisModel
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} payload
|
*/
|
updateAxisPointer: function (axisModel, ecModel, api, payload, force) {
|
updateAxisPointer(this, axisModel, ecModel, api, payload, false);
|
},
|
|
/**
|
* @override
|
*/
|
remove: function (ecModel, api) {
|
var axisPointer = this._axisPointer;
|
axisPointer && axisPointer.remove(api);
|
AxisView.superApply(this, 'remove', arguments);
|
},
|
|
/**
|
* @override
|
*/
|
dispose: function (ecModel, api) {
|
disposeAxisPointer(this, api);
|
AxisView.superApply(this, 'dispose', arguments);
|
}
|
|
});
|
|
function updateAxisPointer(axisView, axisModel, ecModel, api, payload, forceRender) {
|
var Clazz = AxisView.getAxisPointerClass(axisView.axisPointerClass);
|
if (!Clazz) {
|
return;
|
}
|
var axisPointerModel = axisPointerModelHelper.getAxisPointerModel(axisModel);
|
axisPointerModel
|
? (axisView._axisPointer || (axisView._axisPointer = new Clazz()))
|
.render(axisModel, axisPointerModel, api, forceRender)
|
: disposeAxisPointer(axisView, api);
|
}
|
|
function disposeAxisPointer(axisView, ecModel, api) {
|
var axisPointer = axisView._axisPointer;
|
axisPointer && axisPointer.dispose(ecModel, api);
|
axisView._axisPointer = null;
|
}
|
|
var axisPointerClazz = [];
|
|
AxisView.registerAxisPointerClass = function (type, clazz) {
|
if (true) {
|
if (axisPointerClazz[type]) {
|
throw new Error('axisPointer ' + type + ' exists');
|
}
|
}
|
axisPointerClazz[type] = clazz;
|
};
|
|
AxisView.getAxisPointerClass = function (type) {
|
return type && axisPointerClazz[type];
|
};
|
|
module.exports = AxisView;
|
|
|
/***/ },
|
/* 137 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var Model = __webpack_require__(12);
|
var each = zrUtil.each;
|
var curry = zrUtil.curry;
|
|
var helper = {};
|
|
// Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
|
// allAxesInfo should be updated when setOption performed.
|
helper.collect = function (ecModel, api) {
|
var result = {
|
/**
|
* key: makeKey(axis.model)
|
* value: {
|
* axis,
|
* coordSys,
|
* axisPointerModel,
|
* triggerTooltip,
|
* involveSeries,
|
* snap,
|
* seriesModels,
|
* seriesDataCount
|
* }
|
*/
|
axesInfo: {},
|
seriesInvolved: false,
|
/**
|
* key: makeKey(coordSys.model)
|
* value: Object: key makeKey(axis.model), value: axisInfo
|
*/
|
coordSysAxesInfo: {},
|
coordSysMap: {}
|
};
|
|
collectAxesInfo(result, ecModel, api);
|
|
// Check seriesInvolved for performance, in case too many series in some chart.
|
result.seriesInvolved && collectSeriesInfo(result, ecModel);
|
|
return result;
|
};
|
|
function collectAxesInfo(result, ecModel, api) {
|
var globalTooltipModel = ecModel.getComponent('tooltip');
|
var globalAxisPointerModel = ecModel.getComponent('axisPointer');
|
// links can only be set on global.
|
var linksOption = globalAxisPointerModel.get('link', true) || [];
|
var linkGroups = [];
|
|
// Collect axes info.
|
each(api.getCoordinateSystems(), function (coordSys) {
|
// Some coordinate system do not support axes, like geo.
|
if (!coordSys.axisPointerEnabled) {
|
return;
|
}
|
|
var coordSysKey = makeKey(coordSys.model);
|
var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {};
|
result.coordSysMap[coordSysKey] = coordSys;
|
|
// Set tooltip (like 'cross') is a convienent way to show axisPointer
|
// for user. So we enable seting tooltip on coordSys model.
|
var coordSysModel = coordSys.model;
|
var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel);
|
|
each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null));
|
|
// If axis tooltip used, choose tooltip axis for each coordSys.
|
// Notice this case: coordSys is `grid` but not `cartesian2D` here.
|
if (coordSys.getTooltipAxes
|
&& globalTooltipModel
|
// If tooltip.showContent is set as false, tooltip will not
|
// show but axisPointer will show as normal.
|
&& baseTooltipModel.get('show')
|
) {
|
// Compatible with previous logic. But series.tooltip.trigger: 'axis'
|
// or series.data[n].tooltip.trigger: 'axis' are not support any more.
|
var triggerAxis = baseTooltipModel.get('trigger') === 'axis';
|
var cross = baseTooltipModel.get('axisPointer.type') === 'cross';
|
var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get('axisPointer.axis'));
|
if (triggerAxis || cross) {
|
each(tooltipAxes.baseAxes, curry(
|
saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis
|
));
|
}
|
if (cross) {
|
each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false));
|
}
|
}
|
|
// fromTooltip: true | false | 'cross'
|
// triggerTooltip: true | false | null
|
function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) {
|
var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel);
|
|
var axisPointerShow = axisPointerModel.get('show');
|
if (!axisPointerShow || (
|
axisPointerShow === 'auto'
|
&& !fromTooltip
|
&& !isHandleTrigger(axisPointerModel)
|
)) {
|
return;
|
}
|
|
if (triggerTooltip == null) {
|
triggerTooltip = axisPointerModel.get('triggerTooltip');
|
}
|
|
axisPointerModel = fromTooltip
|
? makeAxisPointerModel(
|
axis, baseTooltipModel, globalAxisPointerModel, ecModel,
|
fromTooltip, triggerTooltip
|
)
|
: axisPointerModel;
|
|
var snap = axisPointerModel.get('snap');
|
var key = makeKey(axis.model);
|
var involveSeries = triggerTooltip || snap || axis.type === 'category';
|
|
// If result.axesInfo[key] exist, override it (tooltip has higher priority).
|
var axisInfo = result.axesInfo[key] = {
|
key: key,
|
axis: axis,
|
coordSys: coordSys,
|
axisPointerModel: axisPointerModel,
|
triggerTooltip: triggerTooltip,
|
involveSeries: involveSeries,
|
snap: snap,
|
useHandle: isHandleTrigger(axisPointerModel),
|
seriesModels: []
|
};
|
axesInfoInCoordSys[key] = axisInfo;
|
result.seriesInvolved |= involveSeries;
|
|
var groupIndex = getLinkGroupIndex(linksOption, axis);
|
if (groupIndex != null) {
|
var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = {axesInfo: {}});
|
linkGroup.axesInfo[key] = axisInfo;
|
linkGroup.mapper = linksOption[groupIndex].mapper;
|
axisInfo.linkGroup = linkGroup;
|
}
|
}
|
});
|
}
|
|
function makeAxisPointerModel(
|
axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip
|
) {
|
var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer');
|
var volatileOption = {};
|
|
each(
|
[
|
'type', 'snap', 'lineStyle', 'shadowStyle', 'label',
|
'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z'
|
],
|
function (field) {
|
volatileOption[field] = zrUtil.clone(tooltipAxisPointerModel.get(field));
|
}
|
);
|
|
// category axis do not auto snap, otherwise some tick that do not
|
// has value can not be hovered. value/time/log axis default snap if
|
// triggered from tooltip and trigger tooltip.
|
volatileOption.snap = axis.type !== 'category' && !!triggerTooltip;
|
|
// Compatibel with previous behavior, tooltip axis do not show label by default.
|
// Only these properties can be overrided from tooltip to axisPointer.
|
if (tooltipAxisPointerModel.get('type') === 'cross') {
|
volatileOption.type = 'line';
|
}
|
var labelOption = volatileOption.label || (volatileOption.label = {});
|
// Follow the convention, do not show label when triggered by tooltip by default.
|
labelOption.show == null && (labelOption.show = false);
|
|
if (fromTooltip === 'cross') {
|
// When 'cross', both axes show labels.
|
labelOption.show = true;
|
// If triggerTooltip, this is a base axis, which should better not use cross style
|
// (cross style is dashed by default)
|
if (!triggerTooltip) {
|
var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle');
|
crossStyle && zrUtil.defaults(
|
labelOption.textStyle || (labelOption.textStyle = {}),
|
crossStyle.textStyle
|
);
|
}
|
}
|
|
return axis.model.getModel(
|
'axisPointer',
|
new Model(volatileOption, globalAxisPointerModel, ecModel)
|
);
|
}
|
|
function collectSeriesInfo(result, ecModel) {
|
// Prepare data for axis trigger
|
ecModel.eachSeries(function (seriesModel) {
|
|
// Notice this case: this coordSys is `cartesian2D` but not `grid`.
|
var coordSys = seriesModel.coordinateSystem;
|
var seriesTooltipTrigger = seriesModel.get('tooltip.trigger', true);
|
if (!coordSys
|
|| seriesTooltipTrigger === 'none'
|
|| seriesTooltipTrigger === false
|
|| seriesTooltipTrigger === 'item'
|
|| seriesModel.get('axisPointer.show', true) === false
|
) {
|
return;
|
}
|
|
each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) {
|
var axis = axisInfo.axis;
|
if (coordSys.getAxis(axis.dim) === axis) {
|
axisInfo.seriesModels.push(seriesModel);
|
axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0);
|
axisInfo.seriesDataCount += seriesModel.getData().count();
|
}
|
});
|
|
}, this);
|
}
|
|
/**
|
* For example:
|
* {
|
* axisPointer: {
|
* links: [{
|
* xAxisIndex: [2, 4],
|
* yAxisIndex: 'all'
|
* }, {
|
* xAxisId: ['a5', 'a7'],
|
* xAxisName: 'xxx'
|
* }]
|
* }
|
* }
|
*/
|
function getLinkGroupIndex(linksOption, axis) {
|
var axisModel = axis.model;
|
var dim = axis.dim;
|
for (var i = 0; i < linksOption.length; i++) {
|
var linkOption = linksOption[i] || {};
|
if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id)
|
|| checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex)
|
|| checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)
|
) {
|
return i;
|
}
|
}
|
}
|
|
function checkPropInLink(linkPropValue, axisPropValue) {
|
return linkPropValue === 'all'
|
|| (zrUtil.isArray(linkPropValue) && zrUtil.indexOf(linkPropValue, axisPropValue) >= 0)
|
|| linkPropValue === axisPropValue;
|
}
|
|
helper.fixValue = function (axisModel) {
|
var axisInfo = helper.getAxisInfo(axisModel);
|
if (!axisInfo) {
|
return;
|
}
|
|
var axisPointerModel = axisInfo.axisPointerModel;
|
var scale = axisInfo.axis.scale;
|
var option = axisPointerModel.option;
|
var status = axisPointerModel.get('status');
|
var value = axisPointerModel.get('value');
|
|
// Parse init value for category and time axis.
|
if (value != null) {
|
value = scale.parse(value);
|
}
|
|
var useHandle = isHandleTrigger(axisPointerModel);
|
// If `handle` used, `axisPointer` will always be displayed, so value
|
// and status should be initialized.
|
if (status == null) {
|
option.status = useHandle ? 'show' : 'hide';
|
}
|
|
var extent = scale.getExtent().slice();
|
extent[0] > extent[1] && extent.reverse();
|
|
if (// Pick a value on axis when initializing.
|
value == null
|
// If both `handle` and `dataZoom` are used, value may be out of axis extent,
|
// where we should re-pick a value to keep `handle` displaying normally.
|
|| value > extent[1]
|
) {
|
// Make handle displayed on the end of the axis when init, which looks better.
|
value = extent[1];
|
}
|
if (value < extent[0]) {
|
value = extent[0];
|
}
|
|
option.value = value;
|
|
if (useHandle) {
|
option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show';
|
}
|
};
|
|
helper.getAxisInfo = function (axisModel) {
|
var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo;
|
return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)];
|
};
|
|
helper.getAxisPointerModel = function (axisModel) {
|
var axisInfo = helper.getAxisInfo(axisModel);
|
return axisInfo && axisInfo.axisPointerModel;
|
};
|
|
function isHandleTrigger(axisPointerModel) {
|
return !!axisPointerModel.get('handle.show');
|
}
|
|
/**
|
* @param {module:echarts/model/Model} model
|
* @return {string} unique key
|
*/
|
var makeKey = helper.makeKey = function (model) {
|
return model.type + '||' + model.id;
|
};
|
|
module.exports = helper;
|
|
|
|
/***/ },
|
/* 138 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
var helper = {};
|
|
/**
|
* @param {Object} opt {labelInside}
|
* @return {Object} {
|
* position, rotation, labelDirection, labelOffset,
|
* tickDirection, labelRotate, labelInterval, z2
|
* }
|
*/
|
helper.layout = function (gridModel, axisModel, opt) {
|
opt = opt || {};
|
var grid = gridModel.coordinateSystem;
|
var axis = axisModel.axis;
|
var layout = {};
|
|
var rawAxisPosition = axis.position;
|
var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition;
|
var axisDim = axis.dim;
|
|
// [left, right, top, bottom]
|
var rect = grid.getRect();
|
var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height];
|
|
var axisOffset = axisModel.get('offset') || 0;
|
|
var posMap = {
|
x: { top: rectBound[2] - axisOffset, bottom: rectBound[3] + axisOffset },
|
y: { left: rectBound[0] - axisOffset, right: rectBound[1] + axisOffset }
|
};
|
|
posMap.x.onZero = Math.max(Math.min(getZero('y'), posMap.x.bottom), posMap.x.top);
|
posMap.y.onZero = Math.max(Math.min(getZero('x'), posMap.y.right), posMap.y.left);
|
|
function getZero(dim, val) {
|
var theAxis = grid.getAxis(dim);
|
return theAxis.toGlobalCoord(theAxis.dataToCoord(0));
|
}
|
|
// Axis position
|
layout.position = [
|
axisDim === 'y' ? posMap.y[axisPosition] : rectBound[0],
|
axisDim === 'x' ? posMap.x[axisPosition] : rectBound[3]
|
];
|
|
// Axis rotation
|
layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1);
|
|
// Tick and label direction, x y is axisDim
|
var dirMap = {top: -1, bottom: 1, left: -1, right: 1};
|
|
layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition];
|
layout.labelOffset = axis.onZero ? posMap[axisDim][rawAxisPosition] - posMap[axisDim].onZero : 0;
|
|
if (axisModel.get('axisTick.inside')) {
|
layout.tickDirection = -layout.tickDirection;
|
}
|
if (zrUtil.retrieve(opt.labelInside, axisModel.get('axisLabel.inside'))) {
|
layout.labelDirection = -layout.labelDirection;
|
}
|
|
// Special label rotation
|
var labelRotate = axisModel.get('axisLabel.rotate');
|
layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate;
|
|
// label interval when auto mode.
|
layout.labelInterval = axis.getLabelInterval();
|
|
// Over splitLine and splitArea
|
layout.z2 = 1;
|
|
return layout;
|
};
|
|
module.exports = helper;
|
|
|
|
/***/ },
|
/* 139 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
|
__webpack_require__(124);
|
|
__webpack_require__(140);
|
__webpack_require__(142);
|
|
var barLayoutGrid = __webpack_require__(145);
|
var echarts = __webpack_require__(1);
|
|
echarts.registerLayout(zrUtil.curry(barLayoutGrid, 'bar'));
|
// Visual coding for legend
|
echarts.registerVisual(function (ecModel) {
|
ecModel.eachSeriesByType('bar', function (seriesModel) {
|
var data = seriesModel.getData();
|
data.setVisual('legendSymbol', 'roundRect');
|
});
|
});
|
|
// In case developer forget to include grid component
|
__webpack_require__(123);
|
|
|
/***/ },
|
/* 140 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(141).extend({
|
|
type: 'series.bar',
|
|
dependencies: ['grid', 'polar'],
|
|
brushSelector: 'rect'
|
});
|
|
|
/***/ },
|
/* 141 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var SeriesModel = __webpack_require__(28);
|
var createListFromArray = __webpack_require__(102);
|
|
module.exports = SeriesModel.extend({
|
|
type: 'series.__base_bar__',
|
|
getInitialData: function (option, ecModel) {
|
if (true) {
|
var coordSys = option.coordinateSystem;
|
if (coordSys !== 'cartesian2d') {
|
throw new Error('Bar only support cartesian2d coordinateSystem');
|
}
|
}
|
return createListFromArray(option.data, this, ecModel);
|
},
|
|
getMarkerPosition: function (value) {
|
var coordSys = this.coordinateSystem;
|
if (coordSys) {
|
// PENDING if clamp ?
|
var pt = coordSys.dataToPoint(value, true);
|
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: '各异'
|
},
|
emphasis: {}
|
}
|
}
|
});
|
|
|
/***/ },
|
/* 142 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var helper = __webpack_require__(143);
|
|
var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'normal', 'barBorderWidth'];
|
|
// FIXME
|
// Just for compatible with ec2.
|
zrUtil.extend(__webpack_require__(12).prototype, __webpack_require__(144));
|
|
var BarView = __webpack_require__(1).extendChartView({
|
|
type: 'bar',
|
|
render: function (seriesModel, ecModel, api) {
|
var coordinateSystemType = seriesModel.get('coordinateSystem');
|
|
if (coordinateSystemType === 'cartesian2d') {
|
this._renderOnCartesian(seriesModel, ecModel, api);
|
}
|
|
return this.group;
|
},
|
|
dispose: zrUtil.noop,
|
|
_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 animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
|
|
data.diff(oldData)
|
.add(function (dataIndex) {
|
if (!data.hasValue(dataIndex)) {
|
return;
|
}
|
|
var itemModel = data.getItemModel(dataIndex);
|
var layout = getRectItemLayout(data, dataIndex, itemModel);
|
var el = createRect(data, dataIndex, itemModel, layout, isHorizontal, animationModel);
|
data.setItemGraphicEl(dataIndex, el);
|
group.add(el);
|
|
updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal);
|
})
|
.update(function (newIndex, oldIndex) {
|
var el = oldData.getItemGraphicEl(oldIndex);
|
|
if (!data.hasValue(newIndex)) {
|
group.remove(el);
|
return;
|
}
|
|
var itemModel = data.getItemModel(newIndex);
|
var layout = getRectItemLayout(data, newIndex, itemModel);
|
|
if (el) {
|
graphic.updateProps(el, {shape: layout}, animationModel, newIndex);
|
}
|
else {
|
el = createRect(data, newIndex, itemModel, layout, isHorizontal, animationModel, true);
|
}
|
|
data.setItemGraphicEl(newIndex, el);
|
// Add back
|
group.add(el);
|
|
updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontal);
|
})
|
.remove(function (dataIndex) {
|
var el = oldData.getItemGraphicEl(dataIndex);
|
el && removeRect(dataIndex, animationModel, el);
|
})
|
.execute();
|
|
this._data = data;
|
},
|
|
remove: function (ecModel, api) {
|
var group = this.group;
|
var data = this._data;
|
if (ecModel.get('animation')) {
|
if (data) {
|
data.eachItemGraphicEl(function (el) {
|
removeRect(el.dataIndex, ecModel, el);
|
});
|
}
|
}
|
else {
|
group.removeAll();
|
}
|
}
|
});
|
|
function createRect(data, dataIndex, itemModel, layout, isHorizontal, animationModel, isUpdate) {
|
var rect = new graphic.Rect({shape: zrUtil.extend({}, layout)});
|
|
// Animation
|
if (animationModel) {
|
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
|
}, animationModel, dataIndex);
|
}
|
|
return rect;
|
}
|
|
function removeRect(dataIndex, animationModel, el) {
|
// Not show text when animating
|
el.style.text = '';
|
graphic.updateProps(el, {
|
shape: {
|
width: 0
|
}
|
}, animationModel, dataIndex, function () {
|
el.parent && el.parent.remove(el);
|
});
|
}
|
|
function getRectItemLayout(data, dataIndex, itemModel) {
|
var layout = data.getItemLayout(dataIndex);
|
var fixedLineWidth = getLineWidth(itemModel, layout);
|
|
// fix layout with lineWidth
|
var signX = layout.width > 0 ? 1 : -1;
|
var signY = layout.height > 0 ? 1 : -1;
|
return {
|
x: layout.x + signX * fixedLineWidth / 2,
|
y: layout.y + signY * fixedLineWidth / 2,
|
width: layout.width - signX * fixedLineWidth,
|
height: layout.height - signY * fixedLineWidth
|
};
|
}
|
|
function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontal) {
|
var color = data.getItemVisual(dataIndex, 'color');
|
var opacity = data.getItemVisual(dataIndex, 'opacity');
|
var itemStyleModel = itemModel.getModel('itemStyle.normal');
|
var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle();
|
|
el.setShape('r', itemStyleModel.get('barBorderRadius') || 0);
|
|
el.useStyle(zrUtil.defaults(
|
{
|
fill: color,
|
opacity: opacity
|
},
|
itemStyleModel.getBarItemStyle()
|
));
|
|
var labelPositionOutside = isHorizontal
|
? (layout.height > 0 ? 'bottom' : 'top')
|
: (layout.width > 0 ? 'left' : 'right');
|
|
helper.setLabel(
|
el.style, hoverStyle, itemModel, color,
|
seriesModel, dataIndex, labelPositionOutside
|
);
|
|
graphic.setHoverStyle(el, hoverStyle);
|
}
|
|
// In case width or height are too small.
|
function getLineWidth(itemModel, rawLayout) {
|
var lineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0;
|
return Math.min(lineWidth, Math.abs(rawLayout.width), Math.abs(rawLayout.height));
|
}
|
|
module.exports = BarView;
|
|
|
/***/ },
|
/* 143 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
|
var helper = {};
|
|
helper.setLabel = function (
|
normalStyle, hoverStyle, itemModel, color, seriesModel, dataIndex, labelPositionOutside
|
) {
|
var labelModel = itemModel.getModel('label.normal');
|
var hoverLabelModel = itemModel.getModel('label.emphasis');
|
|
if (labelModel.get('show')) {
|
setLabel(
|
normalStyle, labelModel, color,
|
zrUtil.retrieve(
|
seriesModel.getFormattedLabel(dataIndex, 'normal'),
|
seriesModel.getRawValue(dataIndex)
|
),
|
labelPositionOutside
|
);
|
}
|
else {
|
normalStyle.text = '';
|
}
|
|
if (hoverLabelModel.get('show')) {
|
setLabel(
|
hoverStyle, hoverLabelModel, color,
|
zrUtil.retrieve(
|
seriesModel.getFormattedLabel(dataIndex, 'emphasis'),
|
seriesModel.getRawValue(dataIndex)
|
),
|
labelPositionOutside
|
);
|
}
|
else {
|
hoverStyle.text = '';
|
}
|
};
|
|
function setLabel(style, model, color, labelText, labelPositionOutside) {
|
graphic.setText(style, model, color);
|
style.text = labelText;
|
if (style.textPosition === 'outside') {
|
style.textPosition = labelPositionOutside;
|
}
|
}
|
|
module.exports = helper;
|
|
|
/***/ },
|
/* 144 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
|
var getBarItemStyle = __webpack_require__(15)(
|
[
|
['fill', 'color'],
|
['stroke', 'borderColor'],
|
['lineWidth', 'borderWidth'],
|
// Compatitable with 2
|
['stroke', 'barBorderColor'],
|
['lineWidth', 'barBorderWidth'],
|
['opacity'],
|
['shadowBlur'],
|
['shadowOffsetX'],
|
['shadowOffsetY'],
|
['shadowColor']
|
]
|
);
|
module.exports = {
|
getBarItemStyle: function (excludes) {
|
var style = getBarItemStyle.call(this, excludes);
|
if (this.getBorderLineDash) {
|
var lineDash = this.getBorderLineDash();
|
lineDash && (style.lineDash = lineDash);
|
}
|
return style;
|
}
|
};
|
|
|
/***/ },
|
/* 145 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
var parsePercent = numberUtil.parsePercent;
|
|
function getSeriesStackId(seriesModel) {
|
return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex;
|
}
|
|
function getAxisKey(axis) {
|
return axis.dim + axis.index;
|
}
|
|
function calBarWidthAndOffset(barSeries, api) {
|
// Columns info on each category axis. Key is cartesian name
|
var columnsMap = {};
|
|
zrUtil.each(barSeries, function (seriesModel, idx) {
|
var data = seriesModel.getData();
|
var cartesian = seriesModel.coordinateSystem;
|
|
var baseAxis = cartesian.getBaseAxis();
|
var axisExtent = baseAxis.getExtent();
|
var bandWidth = baseAxis.type === 'category'
|
? baseAxis.getBandWidth()
|
: (Math.abs(axisExtent[1] - axisExtent[0]) / data.count());
|
|
var columnsOnAxis = columnsMap[getAxisKey(baseAxis)] || {
|
bandWidth: bandWidth,
|
remainedWidth: bandWidth,
|
autoWidthCount: 0,
|
categoryGap: '20%',
|
gap: '30%',
|
stacks: {}
|
};
|
var stacks = columnsOnAxis.stacks;
|
columnsMap[getAxisKey(baseAxis)] = columnsOnAxis;
|
|
var stackId = getSeriesStackId(seriesModel);
|
|
if (!stacks[stackId]) {
|
columnsOnAxis.autoWidthCount++;
|
}
|
stacks[stackId] = stacks[stackId] || {
|
width: 0,
|
maxWidth: 0
|
};
|
|
var barWidth = parsePercent(
|
seriesModel.get('barWidth'), bandWidth
|
);
|
var barMaxWidth = parsePercent(
|
seriesModel.get('barMaxWidth'), bandWidth
|
);
|
var barGap = seriesModel.get('barGap');
|
var barCategoryGap = seriesModel.get('barCategoryGap');
|
|
// Caution: In a single coordinate system, these barGrid attributes
|
// will be shared by series. Consider that they have default values,
|
// only the attributes set on the last series will work.
|
// Do not change this fact unless there will be a break change.
|
|
// TODO
|
if (barWidth && !stacks[stackId].width) {
|
barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth);
|
stacks[stackId].width = barWidth;
|
columnsOnAxis.remainedWidth -= barWidth;
|
}
|
|
barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth);
|
(barGap != null) && (columnsOnAxis.gap = barGap);
|
(barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap);
|
});
|
|
var result = {};
|
|
zrUtil.each(columnsMap, function (columnsOnAxis, coordSysName) {
|
|
result[coordSysName] = {};
|
|
var stacks = columnsOnAxis.stacks;
|
var bandWidth = columnsOnAxis.bandWidth;
|
var categoryGap = parsePercent(columnsOnAxis.categoryGap, bandWidth);
|
var barGapPercent = parsePercent(columnsOnAxis.gap, 1);
|
|
var remainedWidth = columnsOnAxis.remainedWidth;
|
var autoWidthCount = columnsOnAxis.autoWidthCount;
|
var autoWidth = (remainedWidth - categoryGap)
|
/ (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
|
autoWidth = Math.max(autoWidth, 0);
|
|
// Find if any auto calculated bar exceeded maxBarWidth
|
zrUtil.each(stacks, function (column, stack) {
|
var maxWidth = column.maxWidth;
|
if (maxWidth && maxWidth < autoWidth) {
|
maxWidth = Math.min(maxWidth, remainedWidth);
|
if (column.width) {
|
maxWidth = Math.min(maxWidth, column.width);
|
}
|
remainedWidth -= maxWidth;
|
column.width = maxWidth;
|
autoWidthCount--;
|
}
|
});
|
|
// Recalculate width again
|
autoWidth = (remainedWidth - categoryGap)
|
/ (autoWidthCount + (autoWidthCount - 1) * barGapPercent);
|
autoWidth = Math.max(autoWidth, 0);
|
|
var widthSum = 0;
|
var lastColumn;
|
zrUtil.each(stacks, function (column, idx) {
|
if (!column.width) {
|
column.width = autoWidth;
|
}
|
lastColumn = column;
|
widthSum += column.width * (1 + barGapPercent);
|
});
|
if (lastColumn) {
|
widthSum -= lastColumn.width * barGapPercent;
|
}
|
|
var offset = -widthSum / 2;
|
zrUtil.each(stacks, function (column, stackId) {
|
result[coordSysName][stackId] = result[coordSysName][stackId] || {
|
offset: offset,
|
width: column.width
|
};
|
|
offset += column.width * (1 + barGapPercent);
|
});
|
});
|
|
return result;
|
}
|
|
/**
|
* @param {string} seriesType
|
* @param {module:echarts/model/Global} ecModel
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
function barLayoutGrid(seriesType, ecModel, api) {
|
|
var barWidthAndOffset = calBarWidthAndOffset(
|
zrUtil.filter(
|
ecModel.getSeriesByType(seriesType),
|
function (seriesModel) {
|
return !ecModel.isSeriesFiltered(seriesModel)
|
&& seriesModel.coordinateSystem
|
&& seriesModel.coordinateSystem.type === 'cartesian2d';
|
}
|
)
|
);
|
|
var lastStackCoords = {};
|
var lastStackCoordsOrigin = {};
|
|
ecModel.eachSeriesByType(seriesType, function (seriesModel) {
|
|
var data = seriesModel.getData();
|
var cartesian = seriesModel.coordinateSystem;
|
var baseAxis = cartesian.getBaseAxis();
|
|
var stackId = getSeriesStackId(seriesModel);
|
var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId];
|
var columnOffset = columnLayoutInfo.offset;
|
var columnWidth = columnLayoutInfo.width;
|
var valueAxis = cartesian.getOtherAxis(baseAxis);
|
|
var barMinHeight = seriesModel.get('barMinHeight') || 0;
|
|
var valueAxisStart = baseAxis.onZero
|
? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0))
|
: valueAxis.getGlobalExtent()[0];
|
|
var coords = cartesian.dataToPoints(data, true);
|
lastStackCoords[stackId] = lastStackCoords[stackId] || [];
|
lastStackCoordsOrigin[stackId] = lastStackCoordsOrigin[stackId] || []; // Fix #4243
|
|
data.setLayout({
|
offset: columnOffset,
|
size: columnWidth
|
});
|
|
data.each(valueAxis.dim, function (value, idx) {
|
if (isNaN(value)) {
|
return;
|
}
|
|
if (!lastStackCoords[stackId][idx]) {
|
lastStackCoords[stackId][idx] = {
|
p: valueAxisStart, // Positive stack
|
n: valueAxisStart // Negative stack
|
};
|
lastStackCoordsOrigin[stackId][idx] = {
|
p: valueAxisStart, // Positive stack
|
n: valueAxisStart // Negative stack
|
};
|
}
|
var sign = value >= 0 ? 'p' : 'n';
|
var coord = coords[idx];
|
var lastCoord = lastStackCoords[stackId][idx][sign];
|
var lastCoordOrigin = lastStackCoordsOrigin[stackId][idx][sign];
|
var x;
|
var y;
|
var width;
|
var height;
|
|
if (valueAxis.isHorizontal()) {
|
x = lastCoord;
|
y = coord[1] + columnOffset;
|
width = coord[0] - lastCoordOrigin;
|
height = columnWidth;
|
|
lastStackCoordsOrigin[stackId][idx][sign] += width;
|
if (Math.abs(width) < barMinHeight) {
|
width = (width < 0 ? -1 : 1) * barMinHeight;
|
}
|
lastStackCoords[stackId][idx][sign] += width;
|
}
|
else {
|
x = coord[0] + columnOffset;
|
y = lastCoord;
|
width = columnWidth;
|
height = coord[1] - lastCoordOrigin;
|
|
lastStackCoordsOrigin[stackId][idx][sign] += height;
|
if (Math.abs(height) < barMinHeight) {
|
// Include zero to has a positive bar
|
height = (height <= 0 ? -1 : 1) * barMinHeight;
|
}
|
lastStackCoords[stackId][idx][sign] += height;
|
}
|
|
data.setItemLayout(idx, {
|
x: x,
|
y: y,
|
width: width,
|
height: height
|
});
|
}, true);
|
|
}, this);
|
}
|
|
module.exports = barLayoutGrid;
|
|
|
/***/ },
|
/* 146 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var echarts = __webpack_require__(1);
|
|
__webpack_require__(147);
|
__webpack_require__(149);
|
|
__webpack_require__(150)('pie', [{
|
type: 'pieToggleSelect',
|
event: 'pieselectchanged',
|
method: 'toggleSelected'
|
}, {
|
type: 'pieSelect',
|
event: 'pieselected',
|
method: 'select'
|
}, {
|
type: 'pieUnSelect',
|
event: 'pieunselected',
|
method: 'unSelect'
|
}]);
|
|
echarts.registerVisual(zrUtil.curry(__webpack_require__(151), 'pie'));
|
|
echarts.registerLayout(zrUtil.curry(
|
__webpack_require__(152), 'pie'
|
));
|
|
echarts.registerProcessor(zrUtil.curry(__webpack_require__(154), 'pie'));
|
|
|
/***/ },
|
/* 147 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var List = __webpack_require__(98);
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var completeDimensions = __webpack_require__(103);
|
|
var dataSelectableMixin = __webpack_require__(148);
|
|
var PieSeries = __webpack_require__(1).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.getRawData();
|
};
|
|
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.getData();
|
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,
|
// 选择模式,默认关闭,可选single,multiple
|
// selectedMode: false,
|
// 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积)
|
// roseType: null,
|
|
// If still show when all data zero.
|
stillShowZeroSum: true,
|
|
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: {
|
borderWidth: 1
|
},
|
emphasis: {}
|
},
|
|
// Animation type canbe expansion, scale
|
animationType: 'expansion',
|
|
animationEasing: 'cubicOut',
|
|
data: []
|
}
|
});
|
|
zrUtil.mixin(PieSeries, dataSelectableMixin);
|
|
module.exports = PieSeries;
|
|
|
/***/ },
|
/* 148 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Data selectable mixin for chart series.
|
* To eanble data select, option of series must have `selectedMode`.
|
* And each data item will use `selected` to toggle itself selected status
|
*
|
* @module echarts/chart/helper/DataSelectable
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
|
module.exports = {
|
|
updateSelectedMap: function (targetList) {
|
this._selectTargetMap = zrUtil.reduce(targetList || [], function (targetMap, target) {
|
targetMap[target.name] = target;
|
return targetMap;
|
}, {});
|
},
|
/**
|
* @param {string} name
|
*/
|
// PENGING If selectedMode is null ?
|
select: function (name) {
|
var targetMap = this._selectTargetMap;
|
var target = targetMap[name];
|
var selectedMode = this.get('selectedMode');
|
if (selectedMode === 'single') {
|
zrUtil.each(targetMap, function (target) {
|
target.selected = false;
|
});
|
}
|
target && (target.selected = true);
|
},
|
|
/**
|
* @param {string} name
|
*/
|
unSelect: function (name) {
|
var target = this._selectTargetMap[name];
|
// var selectedMode = this.get('selectedMode');
|
// selectedMode !== 'single' && target && (target.selected = false);
|
target && (target.selected = false);
|
},
|
|
/**
|
* @param {string} name
|
*/
|
toggleSelected: function (name) {
|
var target = this._selectTargetMap[name];
|
if (target != null) {
|
this[target.selected ? 'unSelect' : 'select'](name);
|
return target.selected;
|
}
|
},
|
|
/**
|
* @param {string} name
|
*/
|
isSelected: function (name) {
|
var target = this._selectTargetMap[name];
|
return target && target.selected;
|
}
|
};
|
|
|
/***/ },
|
/* 149 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var graphic = __webpack_require__(44);
|
var zrUtil = __webpack_require__(4);
|
|
/**
|
* @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);
|
|
var animationType = seriesModel.getShallow('animationType');
|
if (animationType === 'scale') {
|
sector.shape.r = layout.r0;
|
graphic.initProps(sector, {
|
shape: {
|
r: layout.r
|
}
|
}, seriesModel, idx);
|
}
|
// Expansion
|
else {
|
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(
|
{
|
lineJoin: 'bevel',
|
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') && seriesModel.isAnimationEnabled()) {
|
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 = __webpack_require__(43).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 animationType = seriesModel.get('animationType');
|
|
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);
|
// Default expansion animation
|
if (isFirstRender && animationType !== 'scale') {
|
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
|
// Default expansion animation
|
&& animationType !== 'scale'
|
) {
|
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;
|
},
|
|
dispose: function () {},
|
|
_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;
|
},
|
|
/**
|
* @implement
|
*/
|
containPoint: function (point, seriesModel) {
|
var data = seriesModel.getData();
|
var itemLayout = data.getItemLayout(0);
|
if (itemLayout) {
|
var dx = point[0] - itemLayout.cx;
|
var dy = point[1] - itemLayout.cy;
|
var radius = Math.sqrt(dx * dx + dy * dy);
|
return radius <= itemLayout.r && radius >= itemLayout.r0;
|
}
|
}
|
|
});
|
|
module.exports = Pie;
|
|
|
/***/ },
|
/* 150 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var echarts = __webpack_require__(1);
|
var zrUtil = __webpack_require__(4);
|
module.exports = 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
|
};
|
});
|
});
|
};
|
|
|
/***/ },
|
/* 151 */
|
/***/ function(module, exports) {
|
|
// Pick color from palette for each data item.
|
// Applicable for charts that require applying color palette
|
// in data level (like pie, funnel, chord).
|
|
|
module.exports = function (seriesType, ecModel) {
|
// Pie and funnel may use diferrent scope
|
var paletteScope = {};
|
ecModel.eachRawSeriesByType(seriesType, function (seriesModel) {
|
var dataAll = seriesModel.getRawData();
|
var idxMap = {};
|
if (!ecModel.isSeriesFiltered(seriesModel)) {
|
var data = seriesModel.getData();
|
data.each(function (idx) {
|
var rawIdx = data.getRawIndex(idx);
|
idxMap[rawIdx] = idx;
|
});
|
dataAll.each(function (rawIdx) {
|
var filteredIdx = idxMap[rawIdx];
|
|
// If series.itemStyle.normal.color is a function. itemVisual may be encoded
|
var singleDataColor = filteredIdx != null
|
&& data.getItemVisual(filteredIdx, 'color', true);
|
|
if (!singleDataColor) {
|
// FIXME Performance
|
var itemModel = dataAll.getItemModel(rawIdx);
|
var color = itemModel.get('itemStyle.normal.color')
|
|| seriesModel.getColorFromPalette(dataAll.getName(rawIdx), paletteScope);
|
// Legend may use the visual info in data before processed
|
dataAll.setItemVisual(rawIdx, 'color', color);
|
|
// Data is not filtered
|
if (filteredIdx != null) {
|
data.setItemVisual(filteredIdx, 'color', color);
|
}
|
}
|
else {
|
// Set data all color for legend
|
dataAll.setItemVisual(rawIdx, 'color', singleDataColor);
|
}
|
});
|
}
|
});
|
};
|
|
|
/***/ },
|
/* 152 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// TODO minAngle
|
|
|
|
var numberUtil = __webpack_require__(7);
|
var parsePercent = numberUtil.parsePercent;
|
var labelLayout = __webpack_require__(153);
|
var zrUtil = __webpack_require__(4);
|
|
var PI2 = Math.PI * 2;
|
var RADIAN = Math.PI / 180;
|
|
module.exports = function (seriesType, ecModel, api, payload) {
|
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');
|
var stillShowZeroSum = seriesModel.get('stillShowZeroSum');
|
|
// [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;
|
if (isNaN(value)) {
|
data.setItemLayout(idx, {
|
angle: NaN,
|
startAngle: NaN,
|
endAngle: NaN,
|
clockwise: clockwise,
|
cx: cx,
|
cy: cy,
|
r0: r0,
|
r: roseType
|
? NaN
|
: r
|
});
|
return;
|
}
|
|
// FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样?
|
if (roseType !== 'area') {
|
angle = (sum === 0 && stillShowZeroSum)
|
? 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 += dir * angle;
|
});
|
}
|
}
|
|
labelLayout(seriesModel, r, width, height);
|
});
|
};
|
|
|
/***/ },
|
/* 153 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
// FIXME emphasis label position is not same with normal label position
|
|
|
var textContain = __webpack_require__(8);
|
|
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;
|
}
|
}
|
}
|
|
module.exports = 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);
|
}
|
};
|
|
|
/***/ },
|
/* 154 */
|
/***/ function(module, exports) {
|
|
|
module.exports = function (seriesType, ecModel) {
|
var legendModels = ecModel.findComponents({
|
mainType: 'legend'
|
});
|
if (!legendModels || !legendModels.length) {
|
return;
|
}
|
ecModel.eachSeriesByType(seriesType, function (series) {
|
var data = series.getData();
|
data.filterSelf(function (idx) {
|
var name = data.getName(idx);
|
// If in any legend component the status is not selected.
|
for (var i = 0; i < legendModels.length; i++) {
|
if (!legendModels[i].isSelected(name)) {
|
return false;
|
}
|
}
|
return true;
|
}, this);
|
}, this);
|
};
|
|
|
/***/ },
|
/* 155 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var echarts = __webpack_require__(1);
|
|
__webpack_require__(156);
|
__webpack_require__(157);
|
|
echarts.registerVisual(zrUtil.curry(
|
__webpack_require__(120), 'scatter', 'circle', null
|
));
|
echarts.registerLayout(zrUtil.curry(
|
__webpack_require__(121), 'scatter'
|
));
|
|
// In case developer forget to include grid component
|
__webpack_require__(123);
|
|
|
/***/ },
|
/* 156 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var createListFromArray = __webpack_require__(102);
|
var SeriesModel = __webpack_require__(28);
|
|
module.exports = SeriesModel.extend({
|
|
type: 'series.scatter',
|
|
dependencies: ['grid', 'polar', 'geo', 'singleAxis'],
|
|
getInitialData: function (option, ecModel) {
|
var list = createListFromArray(option.data, this, ecModel);
|
return list;
|
},
|
|
brushSelector: 'point',
|
|
defaultOption: {
|
coordinateSystem: 'cartesian2d',
|
zlevel: 0,
|
z: 2,
|
legendHoverLink: true,
|
|
hoverAnimation: true,
|
// 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,
|
|
// label: {
|
// normal: {
|
// show: false
|
// distance: 5,
|
// formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
|
// position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
|
// 'inside'|'left'|'right'|'top'|'bottom'
|
// textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE
|
// }
|
// },
|
itemStyle: {
|
normal: {
|
opacity: 0.8
|
// color: 各异
|
}
|
}
|
}
|
|
});
|
|
|
/***/ },
|
/* 157 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var SymbolDraw = __webpack_require__(116);
|
var LargeSymbolDraw = __webpack_require__(158);
|
|
__webpack_require__(1).extendChartView({
|
|
type: 'scatter',
|
|
init: function () {
|
this._normalSymbolDraw = new SymbolDraw();
|
this._largeSymbolDraw = new LargeSymbolDraw();
|
},
|
|
render: function (seriesModel, ecModel, api) {
|
var data = seriesModel.getData();
|
var largeSymbolDraw = this._largeSymbolDraw;
|
var normalSymbolDraw = this._normalSymbolDraw;
|
var group = this.group;
|
|
var symbolDraw = seriesModel.get('large') && data.count() > seriesModel.get('largeThreshold')
|
? largeSymbolDraw : normalSymbolDraw;
|
|
this._symbolDraw = symbolDraw;
|
symbolDraw.updateData(data);
|
group.add(symbolDraw.group);
|
|
group.remove(
|
symbolDraw === largeSymbolDraw
|
? normalSymbolDraw.group : largeSymbolDraw.group
|
);
|
},
|
|
updateLayout: function (seriesModel) {
|
this._symbolDraw.updateLayout(seriesModel);
|
},
|
|
remove: function (ecModel, api) {
|
this._symbolDraw && this._symbolDraw.remove(api, true);
|
},
|
|
dispose: function () {}
|
});
|
|
|
/***/ },
|
/* 158 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// TODO Batch by color
|
|
|
|
var graphic = __webpack_require__(44);
|
var symbolUtil = __webpack_require__(104);
|
|
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, true);
|
}
|
}
|
},
|
|
findDataIndex: function (x, y) {
|
var shape = this.shape;
|
var points = shape.points;
|
var sizes = shape.sizes;
|
|
// Not consider transform
|
// Treat each element as a rect
|
// top down traverse
|
for (var i = points.length - 1; i >= 0; i--) {
|
var pt = points[i];
|
var size = sizes[i];
|
var x0 = pt[0] - size[0] / 2;
|
var y0 = pt[1] - size[1] / 2;
|
if (x >= x0 && y >= y0 && x <= x0 + size[0] && y <= y0 + size[1]) {
|
// i is dataIndex
|
return i;
|
}
|
}
|
|
return -1;
|
}
|
});
|
|
function LargeSymbolDraw() {
|
this.group = new graphic.Group();
|
|
this._symbolEl = new LargeSymbolPath({
|
// rectHover: true,
|
// cursor: 'default'
|
});
|
}
|
|
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 (!(size instanceof Array)) {
|
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);
|
}
|
|
// Enable tooltip
|
// PENDING May have performance issue when path is extremely large
|
symbolEl.seriesIndex = seriesModel.seriesIndex;
|
symbolEl.on('mousemove', function (e) {
|
symbolEl.dataIndex = null;
|
var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY);
|
if (dataIndex > 0) {
|
// Provide dataIndex for tooltip
|
symbolEl.dataIndex = dataIndex;
|
}
|
});
|
|
// Add back
|
this.group.add(symbolEl);
|
};
|
|
largeSymbolProto.updateLayout = function (seriesModel) {
|
var data = seriesModel.getData();
|
this._symbolEl.setShape({
|
points: data.mapArray(data.getItemLayout)
|
});
|
};
|
|
largeSymbolProto.remove = function () {
|
this.group.removeAll();
|
};
|
|
module.exports = LargeSymbolDraw;
|
|
|
/***/ },
|
/* 159 */,
|
/* 160 */,
|
/* 161 */,
|
/* 162 */,
|
/* 163 */,
|
/* 164 */,
|
/* 165 */,
|
/* 166 */,
|
/* 167 */,
|
/* 168 */,
|
/* 169 */,
|
/* 170 */,
|
/* 171 */,
|
/* 172 */,
|
/* 173 */,
|
/* 174 */,
|
/* 175 */,
|
/* 176 */,
|
/* 177 */,
|
/* 178 */,
|
/* 179 */,
|
/* 180 */,
|
/* 181 */,
|
/* 182 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/component/helper/RoamController
|
*/
|
|
|
var Eventful = __webpack_require__(33);
|
var zrUtil = __webpack_require__(4);
|
var eventTool = __webpack_require__(88);
|
var interactionMutex = __webpack_require__(183);
|
|
/**
|
* @alias module:echarts/component/helper/RoamController
|
* @constructor
|
* @mixin {module:zrender/mixin/Eventful}
|
*
|
* @param {module:zrender/zrender~ZRender} zr
|
*/
|
function RoamController(zr) {
|
|
/**
|
* @type {Function}
|
*/
|
this.pointerChecker;
|
|
/**
|
* @type {module:zrender}
|
*/
|
this._zr = zr;
|
|
// Avoid two roamController bind the same handler
|
var bind = zrUtil.bind;
|
var mousedownHandler = bind(mousedown, this);
|
var mousemoveHandler = bind(mousemove, this);
|
var mouseupHandler = bind(mouseup, this);
|
var mousewheelHandler = bind(mousewheel, this);
|
var pinchHandler = bind(pinch, this);
|
|
Eventful.call(this);
|
|
/**
|
* @param {Function} pointerChecker
|
* input: x, y
|
* output: boolean
|
*/
|
this.setPointerChecker = function (pointerChecker) {
|
this.pointerChecker = pointerChecker;
|
};
|
|
/**
|
* Notice: only enable needed types. For example, if 'zoom'
|
* is not needed, 'zoom' should not be enabled, otherwise
|
* default mousewheel behaviour (scroll page) will be disabled.
|
*
|
* @param {boolean|string} [controlType=true] Specify the control type,
|
* which can be null/undefined or true/false
|
* or 'pan/move' or 'zoom'/'scale'
|
*/
|
this.enable = function (controlType) {
|
// Disable previous first
|
this.disable();
|
|
if (controlType == null) {
|
controlType = true;
|
}
|
|
if (controlType === true || (controlType === 'move' || controlType === 'pan')) {
|
zr.on('mousedown', mousedownHandler);
|
zr.on('mousemove', mousemoveHandler);
|
zr.on('mouseup', mouseupHandler);
|
}
|
if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) {
|
zr.on('mousewheel', mousewheelHandler);
|
zr.on('pinch', pinchHandler);
|
}
|
};
|
|
this.disable = function () {
|
zr.off('mousedown', mousedownHandler);
|
zr.off('mousemove', mousemoveHandler);
|
zr.off('mouseup', mouseupHandler);
|
zr.off('mousewheel', mousewheelHandler);
|
zr.off('pinch', pinchHandler);
|
};
|
|
this.dispose = this.disable;
|
|
this.isDragging = function () {
|
return this._dragging;
|
};
|
|
this.isPinching = function () {
|
return this._pinching;
|
};
|
}
|
|
zrUtil.mixin(RoamController, Eventful);
|
|
|
function mousedown(e) {
|
if (e.target && e.target.draggable) {
|
return;
|
}
|
|
var x = e.offsetX;
|
var y = e.offsetY;
|
|
// Only check on mosedown, but not mousemove.
|
// Mouse can be out of target when mouse moving.
|
if (this.pointerChecker && this.pointerChecker(e, x, y)) {
|
this._x = x;
|
this._y = y;
|
this._dragging = true;
|
}
|
}
|
|
function mousemove(e) {
|
if (!this._dragging) {
|
return;
|
}
|
|
eventTool.stop(e.event);
|
|
if (e.gestureEvent !== 'pinch') {
|
|
if (interactionMutex.isTaken(this._zr, 'globalPan')) {
|
return;
|
}
|
|
var x = e.offsetX;
|
var y = e.offsetY;
|
|
var oldX = this._x;
|
var oldY = this._y;
|
|
var dx = x - oldX;
|
var dy = y - oldY;
|
|
this._x = x;
|
this._y = y;
|
|
eventTool.stop(e.event);
|
this.trigger('pan', dx, dy, oldX, oldY, x, y);
|
}
|
}
|
|
function mouseup(e) {
|
this._dragging = false;
|
}
|
|
function mousewheel(e) {
|
// wheelDelta maybe -0 in chrome mac.
|
if (e.wheelDelta === 0) {
|
return;
|
}
|
// Convenience:
|
// Mac and VM Windows on Mac: scroll up: zoom out.
|
// Windows: scroll up: zoom in.
|
var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1;
|
zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY);
|
}
|
|
function pinch(e) {
|
if (interactionMutex.isTaken(this._zr, 'globalPan')) {
|
return;
|
}
|
var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1;
|
zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY);
|
}
|
|
function zoom(e, zoomDelta, zoomX, zoomY) {
|
if (this.pointerChecker && this.pointerChecker(e, zoomX, zoomY)) {
|
// When mouse is out of roamController rect,
|
// default befavoius should be be disabled, otherwise
|
// page sliding is disabled, contrary to expectation.
|
eventTool.stop(e.event);
|
|
this.trigger('zoom', zoomDelta, zoomX, zoomY);
|
}
|
}
|
|
module.exports = RoamController;
|
|
|
/***/ },
|
/* 183 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var ATTR = '\0_ec_interaction_mutex';
|
|
var interactionMutex = {
|
|
take: function (zr, resourceKey, userKey) {
|
var store = getStore(zr);
|
store[resourceKey] = userKey;
|
},
|
|
release: function (zr, resourceKey, userKey) {
|
var store = getStore(zr);
|
var uKey = store[resourceKey];
|
|
if (uKey === userKey) {
|
store[resourceKey] = null;
|
}
|
},
|
|
isTaken: function (zr, resourceKey) {
|
return !!getStore(zr)[resourceKey];
|
}
|
};
|
|
function getStore(zr) {
|
return zr[ATTR] || (zr[ATTR] = {});
|
}
|
|
/**
|
* payload: {
|
* type: 'takeGlobalCursor',
|
* key: 'dataZoomSelect', or 'brush', or ...,
|
* If no userKey, release global cursor.
|
* }
|
*/
|
__webpack_require__(1).registerAction(
|
{type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'},
|
function () {}
|
);
|
|
module.exports = interactionMutex;
|
|
|
/***/ },
|
/* 184 */,
|
/* 185 */
|
/***/ function(module, exports) {
|
|
|
|
var helper = {};
|
|
var IRRELEVANT_EXCLUDES = {'axisPointer': 1, 'tooltip': 1, 'brush': 1};
|
|
/**
|
* Avoid that: mouse click on a elements that is over geo or graph,
|
* but roam is triggered.
|
*/
|
helper.onIrrelevantElement = function (e, api, targetCoordSysModel) {
|
var model = api.getComponentByElement(e.topTarget);
|
// If model is axisModel, it works only if it is injected with coordinateSystem.
|
var coordSys = model && model.coordinateSystem;
|
return model
|
&& model !== targetCoordSysModel
|
&& !IRRELEVANT_EXCLUDES[model.mainType]
|
&& (coordSys && coordSys.model !== targetCoordSysModel);
|
};
|
|
module.exports = helper;
|
|
|
/***/ },
|
/* 186 */,
|
/* 187 */,
|
/* 188 */,
|
/* 189 */,
|
/* 190 */,
|
/* 191 */,
|
/* 192 */,
|
/* 193 */,
|
/* 194 */,
|
/* 195 */,
|
/* 196 */,
|
/* 197 */,
|
/* 198 */,
|
/* 199 */,
|
/* 200 */,
|
/* 201 */,
|
/* 202 */,
|
/* 203 */,
|
/* 204 */,
|
/* 205 */,
|
/* 206 */,
|
/* 207 */,
|
/* 208 */,
|
/* 209 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/chart/helper/LineDraw
|
*/
|
|
|
var graphic = __webpack_require__(44);
|
var LineGroup = __webpack_require__(210);
|
|
|
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;
|
|
var hostModel = lineData.hostModel;
|
|
var seriesScope = {
|
lineStyle: hostModel.getModel('lineStyle.normal').getLineStyle(),
|
hoverLineStyle: hostModel.getModel('lineStyle.emphasis').getLineStyle(),
|
labelModel: hostModel.getModel('label.normal'),
|
hoverLabelModel: hostModel.getModel('label.emphasis')
|
};
|
|
lineData.diff(oldLineData)
|
.add(function (idx) {
|
if (!lineNeedsDraw(lineData.getItemLayout(idx))) {
|
return;
|
}
|
var lineGroup = new LineCtor(lineData, idx, seriesScope);
|
|
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, seriesScope);
|
}
|
else {
|
lineGroup.updateData(lineData, newIdx, seriesScope);
|
}
|
|
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();
|
};
|
|
module.exports = LineDraw;
|
|
|
/***/ },
|
/* 210 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/chart/helper/Line
|
*/
|
|
|
var symbolUtil = __webpack_require__(104);
|
var vector = __webpack_require__(10);
|
// var matrix = require('zrender/lib/core/matrix');
|
var LinePath = __webpack_require__(211);
|
var graphic = __webpack_require__(44);
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
|
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];
|
}
|
else {
|
targetShape.cpx1 = NaN;
|
targetShape.cpy1 = NaN;
|
}
|
}
|
|
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, seriesScope) {
|
graphic.Group.call(this);
|
|
this._createLine(lineData, idx, seriesScope);
|
}
|
|
var lineProto = Line.prototype;
|
|
// Update symbol position and rotation
|
lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
|
|
lineProto._createLine = function (lineData, idx, seriesScope) {
|
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, seriesScope);
|
};
|
|
lineProto.updateData = function (lineData, idx, seriesScope) {
|
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) {
|
this.remove(this.childOfName(symbolCategory));
|
var symbol = createSymbol(symbolCategory, lineData, idx);
|
this.add(symbol);
|
}
|
this[key] = symbolType;
|
}, this);
|
|
this._updateCommonStl(lineData, idx, seriesScope);
|
};
|
|
lineProto._updateCommonStl = function (lineData, idx, seriesScope) {
|
var seriesModel = lineData.hostModel;
|
|
var line = this.childOfName('line');
|
|
var lineStyle = seriesScope && seriesScope.lineStyle;
|
var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
|
var labelModel = seriesScope && seriesScope.labelModel;
|
var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel;
|
|
// Optimization for large dataset
|
if (!seriesScope || lineData.hasItemOption) {
|
var itemModel = lineData.getItemModel(idx);
|
|
lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle();
|
hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle();
|
|
labelModel = itemModel.getModel('label.normal');
|
hoverLabelModel = itemModel.getModel('label.emphasis');
|
}
|
|
var visualColor = lineData.getItemVisual(idx, 'color');
|
var visualOpacity = zrUtil.retrieve(
|
lineData.getItemVisual(idx, 'opacity'),
|
lineStyle.opacity,
|
1
|
);
|
|
line.useStyle(zrUtil.defaults(
|
{
|
strokeNoScale: true,
|
fill: 'none',
|
stroke: visualColor,
|
opacity: visualOpacity
|
},
|
lineStyle
|
));
|
line.hoverStyle = hoverLineStyle;
|
|
// Update symbol
|
zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
|
var symbol = this.childOfName(symbolCategory);
|
if (symbol) {
|
symbol.setColor(visualColor);
|
symbol.setStyle({
|
opacity: visualOpacity
|
});
|
}
|
}, this);
|
|
var showLabel = labelModel.getShallow('show');
|
var hoverShowLabel = hoverLabelModel.getShallow('show');
|
|
var label = this.childOfName('label');
|
var defaultLabelColor;
|
var defaultText;
|
|
if (showLabel || hoverShowLabel) {
|
var rawVal = seriesModel.getRawValue(idx);
|
defaultText = rawVal == null
|
? defaultText = lineData.getName(idx)
|
: isFinite(rawVal)
|
? numberUtil.round(rawVal)
|
: rawVal;
|
defaultLabelColor = visualColor || '#000';
|
}
|
|
// label.afterUpdate = lineAfterUpdate;
|
if (showLabel) {
|
var textStyleModel = labelModel.getModel('textStyle');
|
label.setStyle({
|
text: zrUtil.retrieve(
|
seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType),
|
defaultText
|
),
|
textFont: textStyleModel.getFont(),
|
fill: textStyleModel.getTextColor() || defaultLabelColor
|
});
|
|
label.__textAlign = textStyleModel.get('align');
|
label.__verticalAlign = textStyleModel.get('baseline');
|
label.__position = labelModel.get('position');
|
}
|
else {
|
label.setStyle('text', '');
|
}
|
if (hoverShowLabel) {
|
var textStyleHoverModel = hoverLabelModel.getModel('textStyle');
|
|
label.hoverStyle = {
|
text: zrUtil.retrieve(
|
seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType),
|
defaultText
|
),
|
textFont: textStyleHoverModel.getFont(),
|
fill: textStyleHoverModel.getTextColor() || defaultLabelColor
|
};
|
}
|
else {
|
label.hoverStyle = {
|
text: ''
|
};
|
}
|
|
label.ignore = !showLabel && !hoverShowLabel;
|
|
graphic.setHoverStyle(this);
|
};
|
|
lineProto.updateLayout = function (lineData, idx) {
|
this.setLinePoints(lineData.getItemLayout(idx));
|
};
|
|
lineProto.setLinePoints = function (points) {
|
var linePath = this.childOfName('line');
|
setLinePoints(linePath.shape, points);
|
linePath.dirty();
|
};
|
|
zrUtil.inherits(Line, graphic.Group);
|
|
module.exports = Line;
|
|
|
/***/ },
|
/* 211 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Line path for bezier and straight line draw
|
*/
|
|
var graphic = __webpack_require__(44);
|
var vec2 = __webpack_require__(10);
|
|
var straightLineProto = graphic.Line.prototype;
|
var bezierCurveProto = graphic.BezierCurve.prototype;
|
|
function isLine(shape) {
|
return isNaN(+shape.cpx1) || isNaN(+shape.cpy1);
|
}
|
|
module.exports = 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);
|
}
|
});
|
|
|
/***/ },
|
/* 212 */,
|
/* 213 */,
|
/* 214 */,
|
/* 215 */,
|
/* 216 */,
|
/* 217 */,
|
/* 218 */,
|
/* 219 */,
|
/* 220 */,
|
/* 221 */,
|
/* 222 */,
|
/* 223 */,
|
/* 224 */,
|
/* 225 */,
|
/* 226 */,
|
/* 227 */,
|
/* 228 */,
|
/* 229 */,
|
/* 230 */,
|
/* 231 */,
|
/* 232 */,
|
/* 233 */,
|
/* 234 */,
|
/* 235 */,
|
/* 236 */,
|
/* 237 */,
|
/* 238 */
|
/***/ function(module, exports) {
|
|
|
|
/**
|
* Calculate slider move result.
|
*
|
* @param {number} delta Move length.
|
* @param {Array.<number>} handleEnds handleEnds[0] and be bigger then handleEnds[1].
|
* handleEnds will be modified in this method.
|
* @param {Array.<number>} extent handleEnds is restricted by extent.
|
* extent[0] should less or equals than extent[1].
|
* @param {string} mode 'rigid': Math.abs(handleEnds[0] - handleEnds[1]) remain unchanged,
|
* 'cross' handleEnds[0] can be bigger then handleEnds[1],
|
* 'push' handleEnds[0] can not be bigger then handleEnds[1],
|
* when they touch, one push other.
|
* @param {number} handleIndex If mode is 'rigid', handleIndex is not required.
|
* @return {Array.<number>} The input handleEnds.
|
*/
|
module.exports = function (delta, handleEnds, extent, mode, handleIndex) {
|
if (!delta) {
|
return handleEnds;
|
}
|
|
if (mode === 'rigid') {
|
delta = getRealDelta(delta, handleEnds, extent);
|
handleEnds[0] += delta;
|
handleEnds[1] += delta;
|
}
|
else {
|
delta = getRealDelta(delta, handleEnds[handleIndex], extent);
|
handleEnds[handleIndex] += delta;
|
|
if (mode === 'push' && handleEnds[0] > handleEnds[1]) {
|
handleEnds[1 - handleIndex] = handleEnds[handleIndex];
|
}
|
}
|
|
return handleEnds;
|
|
function getRealDelta(delta, handleEnds, extent) {
|
var handleMinMax = !handleEnds.length
|
? [handleEnds, handleEnds]
|
: handleEnds.slice();
|
handleEnds[0] > handleEnds[1] && handleMinMax.reverse();
|
|
if (delta < 0 && handleMinMax[0] + delta < extent[0]) {
|
delta = extent[0] - handleMinMax[0];
|
}
|
if (delta > 0 && handleMinMax[1] + delta > extent[1]) {
|
delta = extent[1] - handleMinMax[1];
|
}
|
return delta;
|
}
|
};
|
|
|
/***/ },
|
/* 239 */,
|
/* 240 */,
|
/* 241 */,
|
/* 242 */,
|
/* 243 */,
|
/* 244 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Box selection tool.
|
*
|
* @module echarts/component/helper/BrushController
|
*/
|
|
|
|
var Eventful = __webpack_require__(33);
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var interactionMutex = __webpack_require__(183);
|
var DataDiffer = __webpack_require__(99);
|
|
var curry = zrUtil.curry;
|
var each = zrUtil.each;
|
var map = zrUtil.map;
|
var mathMin = Math.min;
|
var mathMax = Math.max;
|
var mathPow = Math.pow;
|
|
var COVER_Z = 10000;
|
var UNSELECT_THRESHOLD = 6;
|
var MIN_RESIZE_LINE_WIDTH = 6;
|
var MUTEX_RESOURCE_KEY = 'globalPan';
|
|
var DIRECTION_MAP = {
|
w: [0, 0],
|
e: [0, 1],
|
n: [1, 0],
|
s: [1, 1]
|
};
|
var CURSOR_MAP = {
|
w: 'ew',
|
e: 'ew',
|
n: 'ns',
|
s: 'ns',
|
ne: 'nesw',
|
sw: 'nesw',
|
nw: 'nwse',
|
se: 'nwse'
|
};
|
var DEFAULT_BRUSH_OPT = {
|
brushStyle: {
|
lineWidth: 2,
|
stroke: 'rgba(0,0,0,0.3)',
|
fill: 'rgba(0,0,0,0.1)'
|
},
|
transformable: true,
|
brushMode: 'single',
|
removeOnClick: false
|
};
|
|
var baseUID = 0;
|
|
/**
|
* @alias module:echarts/component/helper/BrushController
|
* @constructor
|
* @mixin {module:zrender/mixin/Eventful}
|
* @event module:echarts/component/helper/BrushController#brush
|
* params:
|
* areas: Array.<Array>, coord relates to container group,
|
* If no container specified, to global.
|
* opt {
|
* isEnd: boolean,
|
* removeOnClick: boolean
|
* }
|
*
|
* @param {module:zrender/zrender~ZRender} zr
|
*/
|
function BrushController(zr) {
|
|
if (true) {
|
zrUtil.assert(zr);
|
}
|
|
Eventful.call(this);
|
|
/**
|
* @type {module:zrender/zrender~ZRender}
|
* @private
|
*/
|
this._zr = zr;
|
|
/**
|
* @type {module:zrender/container/Group}
|
* @readOnly
|
*/
|
this.group = new graphic.Group();
|
|
/**
|
* Only for drawing (after enabledBrush).
|
* 'line', 'rect', 'polygon' or false
|
* If passing false/null/undefined, disable brush.
|
* If passing 'auto', determined by panel.defaultBrushType
|
* @private
|
* @type {string}
|
*/
|
this._brushType;
|
|
/**
|
* Only for drawing (after enabledBrush).
|
*
|
* @private
|
* @type {Object}
|
*/
|
this._brushOption;
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._panels;
|
|
/**
|
* @private
|
* @type {Array.<nubmer>}
|
*/
|
this._track = [];
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
this._dragging;
|
|
/**
|
* @private
|
* @type {Array}
|
*/
|
this._covers = [];
|
|
/**
|
* @private
|
* @type {moudule:zrender/container/Group}
|
*/
|
this._creatingCover;
|
|
/**
|
* `true` means global panel
|
* @private
|
* @type {module:zrender/container/Group|boolean}
|
*/
|
this._creatingPanel;
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
this._enableGlobalPan;
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
if (true) {
|
this._mounted;
|
}
|
|
/**
|
* @private
|
* @type {string}
|
*/
|
this._uid = 'brushController_' + baseUID++;
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._handlers = {};
|
each(mouseHandlers, function (handler, eventName) {
|
this._handlers[eventName] = zrUtil.bind(handler, this);
|
}, this);
|
}
|
|
BrushController.prototype = {
|
|
constructor: BrushController,
|
|
/**
|
* If set to null/undefined/false, select disabled.
|
* @param {Object} brushOption
|
* @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
|
* If passing false/null/undefined, disable brush.
|
* If passing 'auto', determined by panel.defaultBrushType.
|
* ('auto' can not be used in global panel)
|
* @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
|
* @param {boolean} [brushOption.transformable=true]
|
* @param {boolean} [brushOption.removeOnClick=false]
|
* @param {Object} [brushOption.brushStyle]
|
* @param {number} [brushOption.brushStyle.width]
|
* @param {number} [brushOption.brushStyle.lineWidth]
|
* @param {string} [brushOption.brushStyle.stroke]
|
* @param {string} [brushOption.brushStyle.fill]
|
* @param {number} [brushOption.z]
|
*/
|
enableBrush: function (brushOption) {
|
if (true) {
|
zrUtil.assert(this._mounted);
|
}
|
|
this._brushType && doDisableBrush(this);
|
brushOption.brushType && doEnableBrush(this, brushOption);
|
|
return this;
|
},
|
|
/**
|
* @param {Array.<Object>} panelOpts If not pass, it is global brush.
|
* Each items: {
|
* panelId, // mandatory.
|
* clipPath, // mandatory. function.
|
* isTargetByCursor, // mandatory. function.
|
* defaultBrushType, // optional, only used when brushType is 'auto'.
|
* getLinearBrushOtherExtent, // optional. function.
|
* }
|
*/
|
setPanels: function (panelOpts) {
|
if (panelOpts && panelOpts.length) {
|
var panels = this._panels = {};
|
zrUtil.each(panelOpts, function (panelOpts) {
|
panels[panelOpts.panelId] = zrUtil.clone(panelOpts);
|
});
|
}
|
else {
|
this._panels = null;
|
}
|
return this;
|
},
|
|
/**
|
* @param {Object} [opt]
|
* @return {boolean} [opt.enableGlobalPan=false]
|
*/
|
mount: function (opt) {
|
opt = opt || {};
|
|
if (true) {
|
this._mounted = true; // should be at first.
|
}
|
|
this._enableGlobalPan = opt.enableGlobalPan;
|
|
var thisGroup = this.group;
|
this._zr.add(thisGroup);
|
|
thisGroup.attr({
|
position: opt.position || [0, 0],
|
rotation: opt.rotation || 0,
|
scale: opt.scale || [1, 1]
|
});
|
this._transform = thisGroup.getLocalTransform();
|
|
return this;
|
},
|
|
eachCover: function (cb, context) {
|
each(this._covers, cb, context);
|
},
|
|
/**
|
* Update covers.
|
* @param {Array.<Object>} brushOptionList Like:
|
* [
|
* {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
|
* {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
|
* ...
|
* ]
|
* `brushType` is required in each cover info. (can not be 'auto')
|
* `id` is not mandatory.
|
* `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
|
* If brushOptionList is null/undefined, all covers removed.
|
*/
|
updateCovers: function (brushOptionList) {
|
if (true) {
|
zrUtil.assert(this._mounted);
|
}
|
|
brushOptionList = zrUtil.map(brushOptionList, function (brushOption) {
|
return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
|
});
|
|
var tmpIdPrefix = '\0-brush-index-';
|
var oldCovers = this._covers;
|
var newCovers = this._covers = [];
|
var controller = this;
|
var creatingCover = this._creatingCover;
|
|
(new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey))
|
.add(addOrUpdate)
|
.update(addOrUpdate)
|
.remove(remove)
|
.execute();
|
|
return this;
|
|
function getKey(brushOption, index) {
|
return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index)
|
+ '-' + brushOption.brushType;
|
}
|
|
function oldGetKey(cover, index) {
|
return getKey(cover.__brushOption, index);
|
}
|
|
function addOrUpdate(newIndex, oldIndex) {
|
var newBrushOption = brushOptionList[newIndex];
|
// Consider setOption in event listener of brushSelect,
|
// where updating cover when creating should be forbiden.
|
if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
|
newCovers[newIndex] = oldCovers[oldIndex];
|
}
|
else {
|
var cover = newCovers[newIndex] = oldIndex != null
|
? (
|
oldCovers[oldIndex].__brushOption = newBrushOption,
|
oldCovers[oldIndex]
|
)
|
: endCreating(controller, createCover(controller, newBrushOption));
|
updateCoverAfterCreation(controller, cover);
|
}
|
}
|
|
function remove(oldIndex) {
|
if (oldCovers[oldIndex] !== creatingCover) {
|
controller.group.remove(oldCovers[oldIndex]);
|
}
|
}
|
},
|
|
unmount: function () {
|
if (true) {
|
if (!this._mounted) {
|
return;
|
}
|
}
|
|
this.enableBrush(false);
|
|
// container may 'removeAll' outside.
|
clearCovers(this);
|
this._zr.remove(this.group);
|
|
if (true) {
|
this._mounted = false; // should be at last.
|
}
|
|
return this;
|
},
|
|
dispose: function () {
|
this.unmount();
|
this.off();
|
}
|
};
|
|
zrUtil.mixin(BrushController, Eventful);
|
|
function doEnableBrush(controller, brushOption) {
|
var zr = controller._zr;
|
|
// Consider roam, which takes globalPan too.
|
if (!controller._enableGlobalPan) {
|
interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid);
|
}
|
|
each(controller._handlers, function (handler, eventName) {
|
zr.on(eventName, handler);
|
});
|
|
controller._brushType = brushOption.brushType;
|
controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
|
}
|
|
function doDisableBrush(controller) {
|
var zr = controller._zr;
|
|
interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid);
|
|
each(controller._handlers, function (handler, eventName) {
|
zr.off(eventName, handler);
|
});
|
|
controller._brushType = controller._brushOption = null;
|
}
|
|
function createCover(controller, brushOption) {
|
var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
|
cover.__brushOption = brushOption;
|
updateZ(cover, brushOption);
|
controller.group.add(cover);
|
return cover;
|
}
|
|
function endCreating(controller, creatingCover) {
|
var coverRenderer = getCoverRenderer(creatingCover);
|
if (coverRenderer.endCreating) {
|
coverRenderer.endCreating(controller, creatingCover);
|
updateZ(creatingCover, creatingCover.__brushOption);
|
}
|
return creatingCover;
|
}
|
|
function updateCoverShape(controller, cover) {
|
var brushOption = cover.__brushOption;
|
getCoverRenderer(cover).updateCoverShape(
|
controller, cover, brushOption.range, brushOption
|
);
|
}
|
|
function updateZ(cover, brushOption) {
|
var z = brushOption.z;
|
z == null && (z = COVER_Z);
|
cover.traverse(function (el) {
|
el.z = z;
|
el.z2 = z; // Consider in given container.
|
});
|
}
|
|
function updateCoverAfterCreation(controller, cover) {
|
getCoverRenderer(cover).updateCommon(controller, cover);
|
updateCoverShape(controller, cover);
|
}
|
|
function getCoverRenderer(cover) {
|
return coverRenderers[cover.__brushOption.brushType];
|
}
|
|
// return target panel or `true` (means global panel)
|
function getPanelByPoint(controller, e, localCursorPoint) {
|
var panels = controller._panels;
|
if (!panels) {
|
return true; // Global panel
|
}
|
var panel;
|
var transform = controller._transform;
|
each(panels, function (pn) {
|
pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn);
|
});
|
return panel;
|
}
|
|
// Return a panel or true
|
function getPanelByCover(controller, cover) {
|
var panels = controller._panels;
|
if (!panels) {
|
return true; // Global panel
|
}
|
var panelId = cover.__brushOption.panelId;
|
// User may give cover without coord sys info,
|
// which is then treated as global panel.
|
return panelId != null ? panels[panelId] : true;
|
}
|
|
function clearCovers(controller) {
|
var covers = controller._covers;
|
var originalLength = covers.length;
|
each(covers, function (cover) {
|
controller.group.remove(cover);
|
}, controller);
|
covers.length = 0;
|
|
return !!originalLength;
|
}
|
|
function trigger(controller, opt) {
|
var areas = map(controller._covers, function (cover) {
|
var brushOption = cover.__brushOption;
|
var range = zrUtil.clone(brushOption.range);
|
return {
|
brushType: brushOption.brushType,
|
panelId: brushOption.panelId,
|
range: range
|
};
|
});
|
|
controller.trigger('brush', areas, {
|
isEnd: !!opt.isEnd,
|
removeOnClick: !!opt.removeOnClick
|
});
|
}
|
|
function shouldShowCover(controller) {
|
var track = controller._track;
|
|
if (!track.length) {
|
return false;
|
}
|
|
var p2 = track[track.length - 1];
|
var p1 = track[0];
|
var dx = p2[0] - p1[0];
|
var dy = p2[1] - p1[1];
|
var dist = mathPow(dx * dx + dy * dy, 0.5);
|
|
return dist > UNSELECT_THRESHOLD;
|
}
|
|
function getTrackEnds(track) {
|
var tail = track.length - 1;
|
tail < 0 && (tail = 0);
|
return [track[0], track[tail]];
|
}
|
|
function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
|
var cover = new graphic.Group();
|
|
cover.add(new graphic.Rect({
|
name: 'main',
|
style: makeStyle(brushOption),
|
silent: true,
|
draggable: true,
|
cursor: 'move',
|
drift: curry(doDrift, controller, cover, 'nswe'),
|
ondragend: curry(trigger, controller, {isEnd: true})
|
}));
|
|
each(
|
edgeNames,
|
function (name) {
|
cover.add(new graphic.Rect({
|
name: name,
|
style: {opacity: 0},
|
draggable: true,
|
silent: true,
|
invisible: true,
|
drift: curry(doDrift, controller, cover, name),
|
ondragend: curry(trigger, controller, {isEnd: true})
|
}));
|
}
|
);
|
|
return cover;
|
}
|
|
function updateBaseRect(controller, cover, localRange, brushOption) {
|
var lineWidth = brushOption.brushStyle.lineWidth || 0;
|
var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH);
|
var x = localRange[0][0];
|
var y = localRange[1][0];
|
var xa = x - lineWidth / 2;
|
var ya = y - lineWidth / 2;
|
var x2 = localRange[0][1];
|
var y2 = localRange[1][1];
|
var x2a = x2 - handleSize + lineWidth / 2;
|
var y2a = y2 - handleSize + lineWidth / 2;
|
var width = x2 - x;
|
var height = y2 - y;
|
var widtha = width + lineWidth;
|
var heighta = height + lineWidth;
|
|
updateRectShape(controller, cover, 'main', x, y, width, height);
|
|
if (brushOption.transformable) {
|
updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
|
updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
|
updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
|
updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);
|
|
updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
|
updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
|
updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
|
updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
|
}
|
}
|
|
function updateCommon(controller, cover) {
|
var brushOption = cover.__brushOption;
|
var transformable = brushOption.transformable;
|
|
var mainEl = cover.childAt(0);
|
mainEl.useStyle(makeStyle(brushOption));
|
mainEl.attr({
|
silent: !transformable,
|
cursor: transformable ? 'move' : 'default'
|
});
|
|
each(
|
['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'],
|
function (name) {
|
var el = cover.childOfName(name);
|
var globalDir = getGlobalDirection(controller, name);
|
|
el && el.attr({
|
silent: !transformable,
|
invisible: !transformable,
|
cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null
|
});
|
}
|
);
|
}
|
|
function updateRectShape(controller, cover, name, x, y, w, h) {
|
var el = cover.childOfName(name);
|
el && el.setShape(pointsToRect(
|
clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
|
));
|
}
|
|
function makeStyle(brushOption) {
|
return zrUtil.defaults({strokeNoScale: true}, brushOption.brushStyle);
|
}
|
|
function formatRectRange(x, y, x2, y2) {
|
var min = [mathMin(x, x2), mathMin(y, y2)];
|
var max = [mathMax(x, x2), mathMax(y, y2)];
|
|
return [
|
[min[0], max[0]], // x range
|
[min[1], max[1]] // y range
|
];
|
}
|
|
function getTransform(controller) {
|
return graphic.getTransform(controller.group);
|
}
|
|
function getGlobalDirection(controller, localDirection) {
|
if (localDirection.length > 1) {
|
localDirection = localDirection.split('');
|
var globalDir = [
|
getGlobalDirection(controller, localDirection[0]),
|
getGlobalDirection(controller, localDirection[1])
|
];
|
(globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
|
return globalDir.join('');
|
}
|
else {
|
var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'};
|
var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'};
|
var globalDir = graphic.transformDirection(
|
map[localDirection], getTransform(controller)
|
);
|
return inverseMap[globalDir];
|
}
|
}
|
|
function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
|
var brushOption = cover.__brushOption;
|
var rectRange = toRectRange(brushOption.range);
|
var localDelta = toLocalDelta(controller, dx, dy);
|
|
each(name.split(''), function (namePart) {
|
var ind = DIRECTION_MAP[namePart];
|
rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
|
});
|
|
brushOption.range = fromRectRange(formatRectRange(
|
rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]
|
));
|
|
updateCoverAfterCreation(controller, cover);
|
trigger(controller, {isEnd: false});
|
}
|
|
function driftPolygon(controller, cover, dx, dy, e) {
|
var range = cover.__brushOption.range;
|
var localDelta = toLocalDelta(controller, dx, dy);
|
|
each(range, function (point) {
|
point[0] += localDelta[0];
|
point[1] += localDelta[1];
|
});
|
|
updateCoverAfterCreation(controller, cover);
|
trigger(controller, {isEnd: false});
|
}
|
|
function toLocalDelta(controller, dx, dy) {
|
var thisGroup = controller.group;
|
var localD = thisGroup.transformCoordToLocal(dx, dy);
|
var localZero = thisGroup.transformCoordToLocal(0, 0);
|
|
return [localD[0] - localZero[0], localD[1] - localZero[1]];
|
}
|
|
function clipByPanel(controller, cover, data) {
|
var panel = getPanelByCover(controller, cover);
|
|
return (panel && panel !== true)
|
? panel.clipPath(data, controller._transform)
|
: zrUtil.clone(data);
|
}
|
|
function pointsToRect(points) {
|
var xmin = mathMin(points[0][0], points[1][0]);
|
var ymin = mathMin(points[0][1], points[1][1]);
|
var xmax = mathMax(points[0][0], points[1][0]);
|
var ymax = mathMax(points[0][1], points[1][1]);
|
|
return {
|
x: xmin,
|
y: ymin,
|
width: xmax - xmin,
|
height: ymax - ymin
|
};
|
}
|
|
function resetCursor(controller, e, localCursorPoint) {
|
// Check active
|
if (!controller._brushType) {
|
return;
|
}
|
|
var zr = controller._zr;
|
var covers = controller._covers;
|
var currPanel = getPanelByPoint(controller, e, localCursorPoint);
|
|
// Check whether in covers.
|
if (!controller._dragging) {
|
for (var i = 0; i < covers.length; i++) {
|
var brushOption = covers[i].__brushOption;
|
if (currPanel
|
&& (currPanel === true || brushOption.panelId === currPanel.panelId)
|
&& coverRenderers[brushOption.brushType].contain(
|
covers[i], localCursorPoint[0], localCursorPoint[1]
|
)
|
) {
|
// Use cursor style set on cover.
|
return;
|
}
|
}
|
}
|
|
currPanel && zr.setCursorStyle('crosshair');
|
}
|
|
function preventDefault(e) {
|
var rawE = e.event;
|
rawE.preventDefault && rawE.preventDefault();
|
}
|
|
function mainShapeContain(cover, x, y) {
|
return cover.childOfName('main').contain(x, y);
|
}
|
|
function updateCoverByMouse(controller, e, localCursorPoint, isEnd) {
|
var creatingCover = controller._creatingCover;
|
var panel = controller._creatingPanel;
|
var thisBrushOption = controller._brushOption;
|
var eventParams;
|
|
controller._track.push(localCursorPoint.slice());
|
|
if (shouldShowCover(controller) || creatingCover) {
|
|
if (panel && !creatingCover) {
|
thisBrushOption.brushMode === 'single' && clearCovers(controller);
|
var brushOption = zrUtil.clone(thisBrushOption);
|
brushOption.brushType = determineBrushType(brushOption.brushType, panel);
|
brushOption.panelId = panel === true ? null : panel.panelId;
|
creatingCover = controller._creatingCover = createCover(controller, brushOption);
|
controller._covers.push(creatingCover);
|
}
|
|
if (creatingCover) {
|
var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
|
var coverBrushOption = creatingCover.__brushOption;
|
|
coverBrushOption.range = coverRenderer.getCreatingRange(
|
clipByPanel(controller, creatingCover, controller._track)
|
);
|
|
if (isEnd) {
|
endCreating(controller, creatingCover);
|
coverRenderer.updateCommon(controller, creatingCover);
|
}
|
|
updateCoverShape(controller, creatingCover);
|
|
eventParams = {isEnd: isEnd};
|
}
|
}
|
else if (
|
isEnd
|
&& thisBrushOption.brushMode === 'single'
|
&& thisBrushOption.removeOnClick
|
) {
|
// Help user to remove covers easily, only by a tiny drag, in 'single' mode.
|
// But a single click do not clear covers, because user may have casual
|
// clicks (for example, click on other component and do not expect covers
|
// disappear).
|
// Only some cover removed, trigger action, but not every click trigger action.
|
if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) {
|
eventParams = {isEnd: isEnd, removeOnClick: true};
|
}
|
}
|
|
return eventParams;
|
}
|
|
function determineBrushType(brushType, panel) {
|
if (brushType === 'auto') {
|
if (true) {
|
zrUtil.assert(
|
panel && panel.defaultBrushType,
|
'MUST have defaultBrushType when brushType is "atuo"'
|
);
|
}
|
return panel.defaultBrushType;
|
}
|
return brushType;
|
}
|
|
var mouseHandlers = {
|
|
mousedown: function (e) {
|
if (this._dragging) {
|
// In case some browser do not support globalOut,
|
// and release mose out side the browser.
|
handleDragEnd.call(this, e);
|
}
|
else if (!e.target || !e.target.draggable) {
|
|
preventDefault(e);
|
|
var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
|
|
this._creatingCover = null;
|
var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint);
|
|
if (panel) {
|
this._dragging = true;
|
this._track = [localCursorPoint.slice()];
|
}
|
}
|
},
|
|
mousemove: function (e) {
|
var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
|
|
resetCursor(this, e, localCursorPoint);
|
|
if (this._dragging) {
|
|
preventDefault(e);
|
|
var eventParams = updateCoverByMouse(this, e, localCursorPoint, false);
|
|
eventParams && trigger(this, eventParams);
|
}
|
},
|
|
mouseup: handleDragEnd //,
|
|
// FIXME
|
// in tooltip, globalout should not be triggered.
|
// globalout: handleDragEnd
|
};
|
|
function handleDragEnd(e) {
|
if (this._dragging) {
|
|
preventDefault(e);
|
|
var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY);
|
var eventParams = updateCoverByMouse(this, e, localCursorPoint, true);
|
|
this._dragging = false;
|
this._track = [];
|
this._creatingCover = null;
|
|
// trigger event shoule be at final, after procedure will be nested.
|
eventParams && trigger(this, eventParams);
|
}
|
}
|
|
/**
|
* key: brushType
|
* @type {Object}
|
*/
|
var coverRenderers = {
|
|
lineX: getLineRenderer(0),
|
|
lineY: getLineRenderer(1),
|
|
rect: {
|
createCover: function (controller, brushOption) {
|
return createBaseRectCover(
|
curry(
|
driftRect,
|
function (range) {
|
return range;
|
},
|
function (range) {
|
return range;
|
}
|
),
|
controller,
|
brushOption,
|
['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']
|
);
|
},
|
getCreatingRange: function (localTrack) {
|
var ends = getTrackEnds(localTrack);
|
return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
|
},
|
updateCoverShape: function (controller, cover, localRange, brushOption) {
|
updateBaseRect(controller, cover, localRange, brushOption);
|
},
|
updateCommon: updateCommon,
|
contain: mainShapeContain
|
},
|
|
polygon: {
|
createCover: function (controller, brushOption) {
|
var cover = new graphic.Group();
|
|
// Do not use graphic.Polygon because graphic.Polyline do not close the
|
// border of the shape when drawing, which is a better experience for user.
|
cover.add(new graphic.Polyline({
|
name: 'main',
|
style: makeStyle(brushOption),
|
silent: true
|
}));
|
|
return cover;
|
},
|
getCreatingRange: function (localTrack) {
|
return localTrack;
|
},
|
endCreating: function (controller, cover) {
|
cover.remove(cover.childAt(0));
|
// Use graphic.Polygon close the shape.
|
cover.add(new graphic.Polygon({
|
name: 'main',
|
draggable: true,
|
drift: curry(driftPolygon, controller, cover),
|
ondragend: curry(trigger, controller, {isEnd: true})
|
}));
|
},
|
updateCoverShape: function (controller, cover, localRange, brushOption) {
|
cover.childAt(0).setShape({
|
points: clipByPanel(controller, cover, localRange)
|
});
|
},
|
updateCommon: updateCommon,
|
contain: mainShapeContain
|
}
|
};
|
|
function getLineRenderer(xyIndex) {
|
return {
|
createCover: function (controller, brushOption) {
|
return createBaseRectCover(
|
curry(
|
driftRect,
|
function (range) {
|
var rectRange = [range, [0, 100]];
|
xyIndex && rectRange.reverse();
|
return rectRange;
|
},
|
function (rectRange) {
|
return rectRange[xyIndex];
|
}
|
),
|
controller,
|
brushOption,
|
[['w', 'e'], ['n', 's']][xyIndex]
|
);
|
},
|
getCreatingRange: function (localTrack) {
|
var ends = getTrackEnds(localTrack);
|
var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]);
|
var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]);
|
|
return [min, max];
|
},
|
updateCoverShape: function (controller, cover, localRange, brushOption) {
|
var otherExtent;
|
// If brushWidth not specified, fit the panel.
|
var panel = getPanelByCover(controller, cover);
|
if (panel !== true && panel.getLinearBrushOtherExtent) {
|
otherExtent = panel.getLinearBrushOtherExtent(
|
xyIndex, controller._transform
|
);
|
}
|
else {
|
var zr = controller._zr;
|
otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]];
|
}
|
var rectRange = [localRange, otherExtent];
|
xyIndex && rectRange.reverse();
|
|
updateBaseRect(controller, cover, rectRange, brushOption);
|
},
|
updateCommon: updateCommon,
|
contain: mainShapeContain
|
};
|
}
|
|
module.exports = BrushController;
|
|
|
/***/ },
|
/* 245 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var cursorHelper = __webpack_require__(185);
|
var BoundingRect = __webpack_require__(9);
|
|
var mathMax = Math.max;
|
var mathMin = Math.min;
|
|
var helper = {};
|
|
helper.makeRectPanelClipPath = function (rect) {
|
rect = normalizeRect(rect);
|
return function (localPoints, transform) {
|
return zrUtil.map(localPoints, function (localPoint) {
|
var x = localPoint[0];
|
x = mathMax(x, rect.x);
|
x = mathMin(x, rect.x + rect.width);
|
var y = localPoint[1];
|
y = mathMax(y, rect.y);
|
y = mathMin(y, rect.y + rect.height);
|
return [x, y];
|
});
|
};
|
};
|
|
helper.makeLinearBrushOtherExtent = function (rect, specifiedXYIndex) {
|
rect = normalizeRect(rect);
|
return function (xyIndex) {
|
var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex;
|
var brushWidth = idx ? rect.width : rect.height;
|
var base = idx ? rect.x : rect.y;
|
return [base, base + (brushWidth || 0)];
|
};
|
};
|
|
helper.makeRectIsTargetByCursor = function (rect, api, targetModel) {
|
rect = normalizeRect(rect);
|
return function (e, localCursorPoint, transform) {
|
return rect.contain(localCursorPoint[0], localCursorPoint[1])
|
&& !cursorHelper.onIrrelevantElement(e, api, targetModel);
|
};
|
};
|
|
// Consider width/height is negative.
|
function normalizeRect(rect) {
|
return BoundingRect.create(rect);
|
}
|
|
module.exports = helper;
|
|
|
|
/***/ },
|
/* 246 */,
|
/* 247 */,
|
/* 248 */,
|
/* 249 */,
|
/* 250 */,
|
/* 251 */,
|
/* 252 */,
|
/* 253 */,
|
/* 254 */,
|
/* 255 */,
|
/* 256 */,
|
/* 257 */,
|
/* 258 */,
|
/* 259 */,
|
/* 260 */,
|
/* 261 */,
|
/* 262 */,
|
/* 263 */,
|
/* 264 */,
|
/* 265 */,
|
/* 266 */,
|
/* 267 */,
|
/* 268 */,
|
/* 269 */,
|
/* 270 */,
|
/* 271 */,
|
/* 272 */,
|
/* 273 */,
|
/* 274 */,
|
/* 275 */,
|
/* 276 */,
|
/* 277 */,
|
/* 278 */,
|
/* 279 */,
|
/* 280 */,
|
/* 281 */,
|
/* 282 */,
|
/* 283 */,
|
/* 284 */,
|
/* 285 */,
|
/* 286 */,
|
/* 287 */,
|
/* 288 */,
|
/* 289 */,
|
/* 290 */,
|
/* 291 */,
|
/* 292 */,
|
/* 293 */,
|
/* 294 */,
|
/* 295 */,
|
/* 296 */,
|
/* 297 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var echarts = __webpack_require__(1);
|
var axisPointerModelHelper = __webpack_require__(137);
|
var axisTrigger = __webpack_require__(298);
|
var zrUtil = __webpack_require__(4);
|
|
__webpack_require__(300);
|
__webpack_require__(301);
|
|
// CartesianAxisPointer is not supposed to be required here. But consider
|
// echarts.simple.js and online build tooltip, which only require gridSimple,
|
// CartesianAxisPointer should be able to required somewhere.
|
__webpack_require__(303);
|
|
echarts.registerPreprocessor(function (option) {
|
// Always has a global axisPointerModel for default setting.
|
if (option) {
|
(!option.axisPointer || option.axisPointer.length === 0)
|
&& (option.axisPointer = {});
|
|
var link = option.axisPointer.link;
|
// Normalize to array to avoid object mergin. But if link
|
// is not set, remain null/undefined, otherwise it will
|
// override existent link setting.
|
if (link && !zrUtil.isArray(link)) {
|
option.axisPointer.link = [link];
|
}
|
}
|
});
|
|
// This process should proformed after coordinate systems created
|
// and series data processed. So put it on statistic processing stage.
|
echarts.registerProcessor(echarts.PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) {
|
// Build axisPointerModel, mergin tooltip.axisPointer model for each axis.
|
// allAxesInfo should be updated when setOption performed.
|
ecModel.getComponent('axisPointer').coordSysAxesInfo
|
= axisPointerModelHelper.collect(ecModel, api);
|
});
|
|
// Broadcast to all views.
|
echarts.registerAction({
|
type: 'updateAxisPointer',
|
event: 'updateAxisPointer',
|
update: ':updateAxisPointer'
|
}, function (payload, ecModel, api) {
|
var outputFinder = axisTrigger(
|
ecModel.getComponent('axisPointer').coordSysAxesInfo,
|
payload.currTrigger,
|
[payload.x, payload.y],
|
payload,
|
payload.dispatchAction || zrUtil.bind(api.dispatchAction, api),
|
ecModel,
|
api,
|
payload.tooltipOption,
|
payload.highDownKey
|
);
|
|
return outputFinder;
|
});
|
|
|
|
/***/ },
|
/* 298 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var modelHelper = __webpack_require__(137);
|
var findPointFromSeries = __webpack_require__(299);
|
|
var each = zrUtil.each;
|
var curry = zrUtil.curry;
|
var get = modelUtil.makeGetter();
|
|
/**
|
* Basic logic: check all axis, if they do not demand show/highlight,
|
* then hide/downplay them.
|
*
|
* @param {Object} coordSysAxesInfo
|
* @param {string} [currTrigger] 'click' | 'mousemove' | 'leave'
|
* @param {Array.<number>} [point] x and y, which are mandatory, specify a point to
|
* tigger axisPointer and tooltip.
|
* @param {Object} [finder] {xAxisId: ...[], yAxisName: ...[], angleAxisIndex: ...[]}
|
* These properties, which are optional, restrict target axes.
|
* @param {Function} dispatchAction
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} [tooltipOption]
|
* @param {string} [highDownKey]
|
* @return {Object} content of event obj for echarts.connect.
|
*/
|
function axisTrigger(
|
coordSysAxesInfo, currTrigger, point, finder, dispatchAction,
|
ecModel, api, tooltipOption, highDownKey
|
) {
|
finder = finder || {};
|
if (!point || point[0] == null || point[1] == null) {
|
point = findPointFromSeries({
|
seriesIndex: finder.seriesIndex,
|
// Do not use dataIndexInside from other ec instance.
|
// FIXME: auto detect it?
|
dataIndex: finder.dataIndex
|
}, ecModel).point;
|
}
|
|
var axesInfo = coordSysAxesInfo.axesInfo;
|
var shouldHide = currTrigger === 'leave' || illegalPoint(point);
|
var outputFinder = {};
|
|
var showValueMap = {};
|
var dataByCoordSys = {list: [], map: {}};
|
var highlightBatch = [];
|
var updaters = {
|
showPointer: curry(showPointer, showValueMap),
|
showTooltip: curry(showTooltip, dataByCoordSys),
|
highlight: curry(highlight, highlightBatch)
|
};
|
|
// Process for triggered axes.
|
each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) {
|
var coordSysContainsPoint = coordSys.containPoint(point);
|
|
each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) {
|
var axis = axisInfo.axis;
|
if (!shouldHide && coordSysContainsPoint && !notTargetAxis(finder, axis)) {
|
processOnAxis(axisInfo, axis.pointToData(point), updaters, false, outputFinder);
|
}
|
});
|
});
|
|
// Process for linked axes.
|
var linkTriggers = {};
|
each(axesInfo, function (tarAxisInfo, tarKey) {
|
var linkGroup = tarAxisInfo.linkGroup;
|
|
// If axis has been triggered in the previous stage, it should not be triggered by link.
|
if (linkGroup && !showValueMap[tarKey]) {
|
each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) {
|
var srcValItem = showValueMap[srcKey];
|
// If srcValItem exist, source axis is triggered, so link to target axis.
|
if (srcAxisInfo !== tarAxisInfo && srcValItem) {
|
var val = srcValItem.value;
|
linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(
|
val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo)
|
)));
|
linkTriggers[tarAxisInfo.key] = val;
|
}
|
});
|
}
|
});
|
each(linkTriggers, function (val, tarKey) {
|
processOnAxis(axesInfo[tarKey], val, updaters, true, outputFinder);
|
});
|
|
updateModelActually(showValueMap, axesInfo);
|
dispatchTooltipActually(dataByCoordSys, point, tooltipOption, dispatchAction);
|
dispatchHighDownActually(highlightBatch, dispatchAction, api, highDownKey);
|
|
return outputFinder;
|
}
|
|
function processOnAxis(axisInfo, newValue, updaters, dontSnap, outputFinder) {
|
var axis = axisInfo.axis;
|
|
if (axis.scale.isBlank() || !axis.containData(newValue)) {
|
return;
|
}
|
|
if (!axisInfo.involveSeries) {
|
updaters.showPointer(axisInfo, newValue);
|
return;
|
}
|
|
// Heavy calculation. So put it after axis.containData checking.
|
var payloadInfo = buildPayloadsBySeries(newValue, axisInfo);
|
var payloadBatch = payloadInfo.payloadBatch;
|
var snapToValue = payloadInfo.snapToValue;
|
|
// Fill content of event obj for echarts.connect.
|
// By defualt use the first involved series data as a sample to connect.
|
if (payloadBatch[0] && outputFinder.seriesIndex == null) {
|
zrUtil.extend(outputFinder, payloadBatch[0]);
|
}
|
|
// If no linkSource input, this process is for collecting link
|
// target, where snap should not be accepted.
|
if (!dontSnap && axisInfo.snap) {
|
if (axis.containData(snapToValue) && snapToValue != null) {
|
newValue = snapToValue;
|
}
|
}
|
|
updaters.highlight('highlight', payloadBatch);
|
updaters.showPointer(axisInfo, newValue, payloadBatch);
|
// Tooltip should always be snapToValue, otherwise there will be
|
// incorrect "axis value ~ series value" mapping displayed in tooltip.
|
updaters.showTooltip(axisInfo, payloadInfo, snapToValue);
|
}
|
|
function buildPayloadsBySeries(value, axisInfo) {
|
var axis = axisInfo.axis;
|
var dim = axis.dim;
|
var snapToValue = value;
|
var payloadBatch = [];
|
var minDist = Number.MAX_VALUE;
|
var minDiff = -1;
|
|
each(axisInfo.seriesModels, function (series, idx) {
|
var dataDim = series.coordDimToDataDim(dim);
|
var seriesNestestValue;
|
var dataIndices;
|
|
if (series.getAxisTooltipData) {
|
var result = series.getAxisTooltipData(dataDim, value, axis);
|
dataIndices = result.dataIndices;
|
seriesNestestValue = result.nestestValue;
|
}
|
else {
|
dataIndices = series.getData().indicesOfNearest(
|
dataDim[0],
|
value,
|
// Add a threshold to avoid find the wrong dataIndex
|
// when data length is not same.
|
false, axis.type === 'category' ? 0.5 : null
|
);
|
if (!dataIndices.length) {
|
return;
|
}
|
seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]);
|
}
|
|
if (seriesNestestValue == null || !isFinite(seriesNestestValue)) {
|
return;
|
}
|
|
var diff = value - seriesNestestValue;
|
var dist = Math.abs(diff);
|
// Consider category case
|
if (dist <= minDist) {
|
if (dist < minDist || (diff >= 0 && minDiff < 0)) {
|
minDist = dist;
|
minDiff = diff;
|
snapToValue = seriesNestestValue;
|
payloadBatch.length = 0;
|
}
|
each(dataIndices, function (dataIndex) {
|
payloadBatch.push({
|
seriesIndex: series.seriesIndex,
|
dataIndexInside: dataIndex,
|
dataIndex: series.getData().getRawIndex(dataIndex)
|
});
|
});
|
}
|
});
|
|
return {
|
payloadBatch: payloadBatch,
|
snapToValue: snapToValue
|
};
|
}
|
|
function showPointer(showValueMap, axisInfo, value, payloadBatch) {
|
showValueMap[axisInfo.key] = {value: value, payloadBatch: payloadBatch};
|
}
|
|
function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) {
|
var payloadBatch = payloadInfo.payloadBatch;
|
var axis = axisInfo.axis;
|
var axisModel = axis.model;
|
var axisPointerModel = axisInfo.axisPointerModel;
|
|
// If no data, do not create anything in dataByCoordSys,
|
// whose length will be used to judge whether dispatch action.
|
if (!axisInfo.triggerTooltip || !payloadBatch.length) {
|
return;
|
}
|
|
var coordSysModel = axisInfo.coordSys.model;
|
var coordSysKey = modelHelper.makeKey(coordSysModel);
|
var coordSysItem = dataByCoordSys.map[coordSysKey];
|
if (!coordSysItem) {
|
coordSysItem = dataByCoordSys.map[coordSysKey] = {
|
coordSysId: coordSysModel.id,
|
coordSysIndex: coordSysModel.componentIndex,
|
coordSysType: coordSysModel.type,
|
coordSysMainType: coordSysModel.mainType,
|
dataByAxis: []
|
};
|
dataByCoordSys.list.push(coordSysItem);
|
}
|
|
coordSysItem.dataByAxis.push({
|
axisDim: axis.dim,
|
axisIndex: axisModel.componentIndex,
|
axisType: axisModel.type,
|
axisId: axisModel.id,
|
value: value,
|
// Caustion: viewHelper.getValueLabel is actually on "view stage", which
|
// depends that all models have been updated. So it should not be performed
|
// here. Considering axisPointerModel used here is volatile, which is hard
|
// to be retrieve in TooltipView, we prepare parameters here.
|
valueLabelOpt: {
|
precision: axisPointerModel.get('label.precision'),
|
formatter: axisPointerModel.get('label.formatter')
|
},
|
seriesDataIndices: payloadBatch.slice()
|
});
|
}
|
|
function highlight(highlightBatch, actionType, batch) {
|
highlightBatch.push.apply(highlightBatch, batch);
|
}
|
|
function updateModelActually(showValueMap, axesInfo) {
|
// Basic logic: If no 'show' required, 'hide' this axisPointer.
|
each(axesInfo, function (axisInfo, key) {
|
var option = axisInfo.axisPointerModel.option;
|
var valItem = showValueMap[key];
|
|
if (valItem) {
|
!axisInfo.useHandle && (option.status = 'show');
|
option.value = valItem.value;
|
// For label formatter param.
|
option.seriesDataIndices = (valItem.payloadBatch || []).slice();
|
}
|
// When always show (e.g., handle used), remain
|
// original value and status.
|
else {
|
// If hide, value still need to be set, consider
|
// click legend to toggle axis blank.
|
!axisInfo.useHandle && (option.status = 'hide');
|
}
|
});
|
}
|
|
function dispatchTooltipActually(dataByCoordSys, point, tooltipOption, dispatchAction) {
|
// Basic logic: If no showTip required, hideTip will be dispatched.
|
if (illegalPoint(point) || !dataByCoordSys.list.length) {
|
dispatchAction({type: 'hideTip'});
|
return;
|
}
|
|
// In most case only one axis (or event one series is used). It is
|
// convinient to fetch payload.seriesIndex and payload.dataIndex
|
// dirtectly. So put the first seriesIndex and dataIndex of the first
|
// axis on the payload.
|
var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {};
|
|
dispatchAction({
|
type: 'showTip',
|
escapeConnect: true,
|
x: point[0],
|
y: point[1],
|
tooltipOption: tooltipOption,
|
dataIndexInside: sampleItem.dataIndexInside,
|
dataIndex: sampleItem.dataIndex,
|
seriesIndex: sampleItem.seriesIndex,
|
dataByCoordSys: dataByCoordSys.list
|
});
|
}
|
|
function dispatchHighDownActually(highlightBatch, dispatchAction, api, highDownKey) {
|
// Basic logic: If nothing highlighted, should downplay all highlighted items.
|
// This case will occur when mouse leave coordSys.
|
|
// FIXME
|
// (1) highlight status shoule be managemented in series.getData()?
|
// (2) If axisPointer A triggerOn 'handle' and axisPointer B triggerOn
|
// 'mousemove', items highlighted by A will be downplayed by B.
|
// It will not be fixed until someone requires this scenario.
|
|
// Consider items area hightlighted by 'handle', and globalListener may
|
// downplay all items (including just highlighted ones) when mousemove.
|
// So we use a highDownKey to separate them as a temporary solution.
|
var zr = api.getZr();
|
highDownKey = 'lastHighlights' + (highDownKey || '');
|
var lastHighlights = get(zr)[highDownKey] || {};
|
var newHighlights = get(zr)[highDownKey] = {};
|
|
// Build hash map and remove duplicate incidentally.
|
zrUtil.each(highlightBatch, function (batchItem) {
|
var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex;
|
newHighlights[key] = batchItem;
|
});
|
|
// Diff.
|
var toHighlight = [];
|
var toDownplay = [];
|
zrUtil.each(lastHighlights, function (batchItem, key) {
|
!newHighlights[key] && toDownplay.push(batchItem);
|
});
|
zrUtil.each(newHighlights, function (batchItem, key) {
|
!lastHighlights[key] && toHighlight.push(batchItem);
|
});
|
|
toDownplay.length && api.dispatchAction({
|
type: 'downplay', escapeConnect: true, batch: toDownplay
|
});
|
toHighlight.length && api.dispatchAction({
|
type: 'highlight', escapeConnect: true, batch: toHighlight
|
});
|
}
|
|
function notTargetAxis(finder, axis) {
|
var isTarget = 1;
|
// If none of xxxAxisId and xxxAxisName and xxxAxisIndex exists in finder,
|
// no axis is not target axis.
|
each(finder, function (value, propName) {
|
isTarget &= !(/^.+(AxisId|AxisName|AxisIndex)$/.test(propName));
|
});
|
!isTarget && each(
|
[['AxisId', 'id'], ['AxisIndex', 'componentIndex'], ['AxisName', 'name']],
|
function (prop) {
|
var vals = modelUtil.normalizeToArray(finder[axis.dim + prop[0]]);
|
isTarget |= zrUtil.indexOf(vals, axis.model[prop[1]]) >= 0;
|
}
|
);
|
return !isTarget;
|
}
|
|
function makeMapperParam(axisInfo) {
|
var axisModel = axisInfo.axis.model;
|
var item = {};
|
var dim = item.axisDim = axisInfo.axis.dim;
|
item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex;
|
item.axisName = item[dim + 'AxisName'] = axisModel.name;
|
item.axisId = item[dim + 'AxisId'] = axisModel.id;
|
return item;
|
}
|
|
function illegalPoint(point) {
|
return point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]);
|
}
|
|
module.exports = axisTrigger;
|
|
|
/***/ },
|
/* 299 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
|
/**
|
* @param {Object} finder contains {seriesIndex, dataIndex, dataIndexInside}
|
* @param {module:echarts/model/Global} ecModel
|
* @return {Object} {point: [x, y], el: ...} point Will not be null.
|
*/
|
module.exports = function (finder, ecModel) {
|
var point = [];
|
var seriesIndex = finder.seriesIndex;
|
var seriesModel;
|
if (seriesIndex == null || !(
|
seriesModel = ecModel.getSeriesByIndex(seriesIndex)
|
)) {
|
return {point: []};
|
}
|
|
var data = seriesModel.getData();
|
var dataIndex = modelUtil.queryDataIndex(data, finder);
|
if (dataIndex == null || zrUtil.isArray(dataIndex)) {
|
return {point: []};
|
}
|
|
var el = data.getItemGraphicEl(dataIndex);
|
var coordSys = seriesModel.coordinateSystem;
|
|
if (seriesModel.getTooltipPosition) {
|
point = seriesModel.getTooltipPosition(dataIndex) || [];
|
}
|
else if (coordSys && coordSys.dataToPoint) {
|
point = coordSys.dataToPoint(
|
data.getValues(
|
zrUtil.map(coordSys.dimensions, function (dim) {
|
return seriesModel.coordDimToDataDim(dim)[0];
|
}), dataIndex, true
|
)
|
) || [];
|
}
|
else if (el) {
|
// Use graphic bounding rect
|
var rect = el.getBoundingRect().clone();
|
rect.applyTransform(el.transform);
|
point = [
|
rect.x + rect.width / 2,
|
rect.y + rect.height / 2
|
];
|
}
|
|
return {point: point, el: el};
|
};
|
|
|
|
|
/***/ },
|
/* 300 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var echarts = __webpack_require__(1);
|
|
var AxisPointerModel = echarts.extendComponentModel({
|
|
type: 'axisPointer',
|
|
coordSysAxesInfo: null,
|
|
defaultOption: {
|
// 'auto' means that show when triggered by tooltip or handle.
|
show: 'auto',
|
// 'click' | 'mousemove' | 'none'
|
triggerOn: null, // set default in AxisPonterView.js
|
|
zlevel: 0,
|
z: 50,
|
|
type: 'line',
|
// axispointer triggered by tootip determine snap automatically,
|
// see `modelHelper`.
|
snap: false,
|
triggerTooltip: true,
|
|
value: null,
|
status: null, // Init value depends on whether handle is used.
|
|
// [group0, group1, ...]
|
// Each group can be: {
|
// mapper: function () {},
|
// singleTooltip: 'multiple', // 'multiple' or 'single'
|
// xAxisId: ...,
|
// yAxisName: ...,
|
// angleAxisIndex: ...
|
// }
|
// mapper: can be ignored.
|
// input: {axisInfo, value}
|
// output: {axisInfo, value}
|
link: [],
|
|
// Do not set 'auto' here, otherwise global animation: false
|
// will not effect at this axispointer.
|
animation: null,
|
animationDurationUpdate: 200,
|
|
lineStyle: {
|
color: '#aaa',
|
width: 1,
|
type: 'solid'
|
},
|
|
shadowStyle: {
|
color: 'rgba(150,150,150,0.3)'
|
},
|
|
label: {
|
show: true,
|
formatter: null, // string | Function
|
precision: 'auto', // Or a number like 0, 1, 2 ...
|
margin: 3,
|
textStyle: {
|
color: '#fff'
|
},
|
padding: [5, 7, 5, 7],
|
backgroundColor: 'auto', // default: axis line color
|
borderColor: null,
|
borderWidth: 0,
|
shadowBlur: 3,
|
shadowColor: '#aaa'
|
// Considering applicability, common style should
|
// better not have shadowOffset.
|
// shadowOffsetX: 0,
|
// shadowOffsetY: 2
|
},
|
|
handle: {
|
show: false,
|
icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', // jshint ignore:line
|
size: 45,
|
// handle margin is from symbol center to axis, which is stable when circular move.
|
margin: 50,
|
// color: '#1b8bbd'
|
// color: '#2f4554'
|
color: '#333',
|
shadowBlur: 3,
|
shadowColor: '#aaa',
|
shadowOffsetX: 0,
|
shadowOffsetY: 2,
|
|
// For mobile performance
|
throttle: 40
|
}
|
}
|
|
});
|
|
module.exports = AxisPointerModel;
|
|
|
|
/***/ },
|
/* 301 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var globalListener = __webpack_require__(302);
|
|
var AxisPonterView = __webpack_require__(1).extendComponentView({
|
|
type: 'axisPointer',
|
|
render: function (globalAxisPointerModel, ecModel, api) {
|
var globalTooltipModel = ecModel.getComponent('tooltip');
|
var triggerOn = globalAxisPointerModel.get('triggerOn')
|
|| (globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click');
|
|
// Register global listener in AxisPointerView to enable
|
// AxisPointerView to be independent to Tooltip.
|
globalListener.register(
|
'axisPointer',
|
api,
|
function (currTrigger, e, dispatchAction) {
|
// If 'none', it is not controlled by mouse totally.
|
if (triggerOn !== 'none'
|
&& (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)
|
) {
|
dispatchAction({
|
type: 'updateAxisPointer',
|
currTrigger: currTrigger,
|
x: e && e.offsetX,
|
y: e && e.offsetY
|
});
|
}
|
}
|
);
|
},
|
|
/**
|
* @override
|
*/
|
remove: function (ecModel, api) {
|
globalListener.disopse(api.getZr(), 'axisPointer');
|
AxisPonterView.superApply(this._model, 'remove', arguments);
|
},
|
|
/**
|
* @override
|
*/
|
dispose: function (ecModel, api) {
|
globalListener.unregister('axisPointer', api);
|
AxisPonterView.superApply(this._model, 'dispose', arguments);
|
}
|
|
});
|
|
|
|
/***/ },
|
/* 302 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var env = __webpack_require__(2);
|
var zrUtil = __webpack_require__(4);
|
var get = __webpack_require__(5).makeGetter();
|
|
var each = zrUtil.each;
|
|
var globalListener = {};
|
|
/**
|
* @param {string} key
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Function} handler
|
* param: {string} currTrigger
|
* param: {Array.<number>} point
|
*/
|
globalListener.register = function (key, api, handler) {
|
if (env.node) {
|
return;
|
}
|
|
var zr = api.getZr();
|
get(zr).records || (get(zr).records = {});
|
|
initGlobalListeners(zr, api);
|
|
var record = get(zr).records[key] || (get(zr).records[key] = {});
|
record.handler = handler;
|
};
|
|
function initGlobalListeners(zr, api) {
|
if (get(zr).initialized) {
|
return;
|
}
|
|
get(zr).initialized = true;
|
|
useHandler('click', zrUtil.curry(doEnter, 'click'));
|
useHandler('mousemove', zrUtil.curry(doEnter, 'mousemove'));
|
// useHandler('mouseout', onLeave);
|
useHandler('globalout', onLeave);
|
|
function useHandler(eventType, cb) {
|
zr.on(eventType, function (e) {
|
var dis = makeDispatchAction(api);
|
|
each(get(zr).records, function (record) {
|
record && cb(record, e, dis.dispatchAction);
|
});
|
|
dispatchTooltipFinally(dis.pendings, api);
|
});
|
}
|
}
|
|
function dispatchTooltipFinally(pendings, api) {
|
var showLen = pendings.showTip.length;
|
var hideLen = pendings.hideTip.length;
|
|
var actuallyPayload;
|
if (showLen) {
|
actuallyPayload = pendings.showTip[showLen - 1];
|
}
|
else if (hideLen) {
|
actuallyPayload = pendings.hideTip[hideLen - 1];
|
}
|
if (actuallyPayload) {
|
actuallyPayload.dispatchAction = null;
|
api.dispatchAction(actuallyPayload);
|
}
|
}
|
|
function onLeave(record, e, dispatchAction) {
|
record.handler('leave', null, dispatchAction);
|
}
|
|
function doEnter(currTrigger, record, e, dispatchAction) {
|
record.handler(currTrigger, e, dispatchAction);
|
}
|
|
function makeDispatchAction(api) {
|
var pendings = {
|
showTip: [],
|
hideTip: []
|
};
|
// FIXME
|
// better approach?
|
// 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip,
|
// which may be conflict, (axisPointer call showTip but tooltip call hideTip);
|
// So we have to add "final stage" to merge those dispatched actions.
|
var dispatchAction = function (payload) {
|
var pendingList = pendings[payload.type];
|
if (pendingList) {
|
pendingList.push(payload);
|
}
|
else {
|
payload.dispatchAction = dispatchAction;
|
api.dispatchAction(payload);
|
}
|
};
|
|
return {
|
dispatchAction: dispatchAction,
|
pendings: pendings
|
};
|
}
|
|
/**
|
* @param {string} key
|
* @param {module:echarts/ExtensionAPI} api
|
*/
|
globalListener.unregister = function (key, api) {
|
if (env.node) {
|
return;
|
}
|
var zr = api.getZr();
|
var record = (get(zr).records || {})[key];
|
if (record) {
|
get(zr).records[key] = null;
|
}
|
};
|
|
module.exports = globalListener;
|
|
|
/***/ },
|
/* 303 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var graphic = __webpack_require__(44);
|
var BaseAxisPointer = __webpack_require__(304);
|
var viewHelper = __webpack_require__(305);
|
var cartesianAxisHelper = __webpack_require__(138);
|
var AxisView = __webpack_require__(136);
|
|
var CartesianAxisPointer = BaseAxisPointer.extend({
|
|
/**
|
* @override
|
*/
|
makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
|
var axis = axisModel.axis;
|
var grid = axis.grid;
|
var axisPointerType = axisPointerModel.get('type');
|
var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
|
var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true));
|
|
if (axisPointerType && axisPointerType !== 'none') {
|
var elStyle = viewHelper.buildElStyle(axisPointerModel);
|
var pointerOption = pointerShapeBuilder[axisPointerType](
|
axis, pixelValue, otherExtent, elStyle
|
);
|
pointerOption.style = elStyle;
|
elOption.graphicKey = pointerOption.type;
|
elOption.pointer = pointerOption;
|
}
|
|
var layoutInfo = cartesianAxisHelper.layout(grid.model, axisModel);
|
viewHelper.buildCartesianSingleLabelElOption(
|
value, elOption, layoutInfo, axisModel, axisPointerModel, api
|
);
|
},
|
|
/**
|
* @override
|
*/
|
getHandleTransform: function (value, axisModel, axisPointerModel) {
|
var layoutInfo = cartesianAxisHelper.layout(axisModel.axis.grid.model, axisModel, {
|
labelInside: false
|
});
|
layoutInfo.labelMargin = axisPointerModel.get('handle.margin');
|
return {
|
position: viewHelper.getTransformedPosition(axisModel.axis, value, layoutInfo),
|
rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
|
};
|
},
|
|
/**
|
* @override
|
*/
|
updateHandleTransform: function (transform, delta, axisModel, axisPointerModel) {
|
var axis = axisModel.axis;
|
var grid = axis.grid;
|
var axisExtent = axis.getGlobalExtent(true);
|
var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
|
var dimIndex = axis.dim === 'x' ? 0 : 1;
|
|
var currPosition = transform.position;
|
currPosition[dimIndex] += delta[dimIndex];
|
currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
|
currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
|
|
var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2;
|
var cursorPoint = [cursorOtherValue, cursorOtherValue];
|
cursorPoint[dimIndex] = currPosition[dimIndex];
|
|
// Make tooltip do not overlap axisPointer and in the middle of the grid.
|
var tooltipOptions = [{verticalAlign: 'middle'}, {align: 'center'}];
|
|
return {
|
position: currPosition,
|
rotation: transform.rotation,
|
cursorPoint: cursorPoint,
|
tooltipOption: tooltipOptions[dimIndex]
|
};
|
}
|
|
});
|
|
function getCartesian(grid, axis) {
|
var opt = {};
|
opt[axis.dim + 'AxisIndex'] = axis.index;
|
return grid.getCartesian(opt);
|
}
|
|
var pointerShapeBuilder = {
|
|
line: function (axis, pixelValue, otherExtent, elStyle) {
|
var targetShape = viewHelper.makeLineShape(
|
[pixelValue, otherExtent[0]],
|
[pixelValue, otherExtent[1]],
|
getAxisDimIndex(axis)
|
);
|
graphic.subPixelOptimizeLine({
|
shape: targetShape,
|
style: elStyle
|
});
|
return {
|
type: 'Line',
|
shape: targetShape
|
};
|
},
|
|
shadow: function (axis, pixelValue, otherExtent, elStyle) {
|
var bandWidth = axis.getBandWidth();
|
var span = otherExtent[1] - otherExtent[0];
|
return {
|
type: 'Rect',
|
shape: viewHelper.makeRectShape(
|
[pixelValue - bandWidth / 2, otherExtent[0]],
|
[bandWidth, span],
|
getAxisDimIndex(axis)
|
)
|
};
|
}
|
};
|
|
function getAxisDimIndex(axis) {
|
return axis.dim === 'x' ? 0 : 1;
|
}
|
|
AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer);
|
|
module.exports = CartesianAxisPointer;
|
|
|
/***/ },
|
/* 304 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var clazzUtil = __webpack_require__(13);
|
var graphic = __webpack_require__(44);
|
var get = __webpack_require__(5).makeGetter();
|
var axisPointerModelHelper = __webpack_require__(137);
|
var eventTool = __webpack_require__(88);
|
var throttle = __webpack_require__(81);
|
|
var clone = zrUtil.clone;
|
var bind = zrUtil.bind;
|
|
/**
|
* Base axis pointer class in 2D.
|
* Implemenents {module:echarts/component/axis/IAxisPointer}.
|
*/
|
function BaseAxisPointer () {
|
}
|
|
BaseAxisPointer.prototype = {
|
|
/**
|
* @private
|
*/
|
_group: null,
|
|
/**
|
* @private
|
*/
|
_lastGraphicKey: null,
|
|
/**
|
* @private
|
*/
|
_handle: null,
|
|
/**
|
* @private
|
*/
|
_dragging: false,
|
|
/**
|
* @private
|
*/
|
_lastValue: null,
|
|
/**
|
* @private
|
*/
|
_lastStatus: null,
|
|
/**
|
* @private
|
*/
|
_payloadInfo: null,
|
|
/**
|
* In px, arbitrary value. Do not set too small,
|
* no animation is ok for most cases.
|
* @protected
|
*/
|
animationThreshold: 15,
|
|
/**
|
* @implement
|
*/
|
render: function (axisModel, axisPointerModel, api, forceRender) {
|
var value = axisPointerModel.get('value');
|
var status = axisPointerModel.get('status');
|
|
// Bind them to `this`, not in closure, otherwise they will not
|
// be replaced when user calling setOption in not merge mode.
|
this._axisModel = axisModel;
|
this._axisPointerModel = axisPointerModel;
|
this._api = api;
|
|
// Optimize: `render` will be called repeatly during mouse move.
|
// So it is power consuming if performing `render` each time,
|
// especially on mobile device.
|
if (!forceRender
|
&& this._lastValue === value
|
&& this._lastStatus === status
|
) {
|
return;
|
}
|
this._lastValue = value;
|
this._lastStatus = status;
|
|
var group = this._group;
|
var handle = this._handle;
|
|
if (!status || status === 'hide') {
|
// Do not clear here, for animation better.
|
group && group.hide();
|
handle && handle.hide();
|
return;
|
}
|
group && group.show();
|
handle && handle.show();
|
|
// Otherwise status is 'show'
|
var elOption = {};
|
this.makeElOption(elOption, value, axisModel, axisPointerModel, api);
|
|
// Enable change axis pointer type.
|
var graphicKey = elOption.graphicKey;
|
if (graphicKey !== this._lastGraphicKey) {
|
this.clear(api);
|
}
|
this._lastGraphicKey = graphicKey;
|
|
var moveAnimation = this._moveAnimation =
|
this.determineAnimation(axisModel, axisPointerModel);
|
|
if (!group) {
|
group = this._group = new graphic.Group();
|
this.createPointerEl(group, elOption, axisModel, axisPointerModel);
|
this.createLabelEl(group, elOption, axisModel, axisPointerModel);
|
api.getZr().add(group);
|
}
|
else {
|
var doUpdateProps = zrUtil.curry(updateProps, axisPointerModel, moveAnimation);
|
this.updatePointerEl(group, elOption, doUpdateProps, axisPointerModel);
|
this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel);
|
}
|
|
updateMandatoryProps(group, axisPointerModel, true);
|
|
this._renderHandle(value);
|
},
|
|
/**
|
* @implement
|
*/
|
remove: function (api) {
|
this.clear(api);
|
},
|
|
/**
|
* @implement
|
*/
|
dispose: function (api) {
|
this.clear(api);
|
},
|
|
/**
|
* @protected
|
*/
|
determineAnimation: function (axisModel, axisPointerModel) {
|
var animation = axisPointerModel.get('animation');
|
var axis = axisModel.axis;
|
var isCategoryAxis = axis.type === 'category';
|
var useSnap = axisPointerModel.get('snap');
|
|
// Value axis without snap always do not snap.
|
if (!useSnap && !isCategoryAxis) {
|
return false;
|
}
|
|
if (animation === 'auto' || animation == null) {
|
var animationThreshold = this.animationThreshold;
|
if (isCategoryAxis && axis.getBandWidth() > animationThreshold) {
|
return true;
|
}
|
|
// It is important to auto animation when snap used. Consider if there is
|
// a dataZoom, animation will be disabled when too many points exist, while
|
// it will be enabled for better visual effect when little points exist.
|
if (useSnap) {
|
var seriesDataCount = axisPointerModelHelper.getAxisInfo(axisModel).seriesDataCount;
|
var axisExtent = axis.getExtent();
|
// Approximate band width
|
return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold;
|
}
|
|
return false;
|
}
|
|
return animation === true;
|
},
|
|
/**
|
* add {pointer, label, graphicKey} to elOption
|
* @protected
|
*/
|
makeElOption: function (elOption, value, axisModel, axisPointerModel, api) {
|
// Shoule be implemenented by sub-class.
|
},
|
|
/**
|
* @protected
|
*/
|
createPointerEl: function (group, elOption, axisModel, axisPointerModel) {
|
var pointerOption = elOption.pointer;
|
if (pointerOption) {
|
var pointerEl = get(group).pointerEl = new graphic[pointerOption.type](
|
clone(elOption.pointer)
|
);
|
group.add(pointerEl);
|
}
|
},
|
|
/**
|
* @protected
|
*/
|
createLabelEl: function (group, elOption, axisModel, axisPointerModel) {
|
if (elOption.label) {
|
var labelEl = get(group).labelEl = new graphic.Rect(
|
clone(elOption.label)
|
);
|
|
group.add(labelEl);
|
updateLabelShowHide(labelEl, axisPointerModel);
|
}
|
},
|
|
/**
|
* @protected
|
*/
|
updatePointerEl: function (group, elOption, updateProps) {
|
var pointerEl = get(group).pointerEl;
|
if (pointerEl) {
|
pointerEl.setStyle(elOption.pointer.style);
|
updateProps(pointerEl, {shape: elOption.pointer.shape});
|
}
|
},
|
|
/**
|
* @protected
|
*/
|
updateLabelEl: function (group, elOption, updateProps, axisPointerModel) {
|
var labelEl = get(group).labelEl;
|
if (labelEl) {
|
labelEl.setStyle(elOption.label.style);
|
updateProps(labelEl, {
|
// Consider text length change in vertical axis, animation should
|
// be used on shape, otherwise the effect will be weird.
|
shape: elOption.label.shape,
|
position: elOption.label.position
|
});
|
|
updateLabelShowHide(labelEl, axisPointerModel);
|
}
|
},
|
|
/**
|
* @private
|
*/
|
_renderHandle: function (value) {
|
if (this._dragging || !this.updateHandleTransform) {
|
return;
|
}
|
|
var axisPointerModel = this._axisPointerModel;
|
var zr = this._api.getZr();
|
var handle = this._handle;
|
var handleModel = axisPointerModel.getModel('handle');
|
|
var status = axisPointerModel.get('status');
|
if (!handleModel.get('show') || !status || status === 'hide') {
|
handle && zr.remove(handle);
|
this._handle = null;
|
return;
|
}
|
|
var isInit;
|
if (!this._handle) {
|
isInit = true;
|
handle = this._handle = createIcon(handleModel, {
|
onmousemove: function (e) {
|
// Fot mobile devicem, prevent screen slider on the button.
|
eventTool.stop(e.event);
|
},
|
onmousedown: bind(this._onHandleDragMove, this, 0, 0),
|
drift: bind(this._onHandleDragMove, this),
|
ondragend: bind(this._onHandleDragEnd, this)
|
});
|
zr.add(handle);
|
}
|
|
updateMandatoryProps(handle, axisPointerModel, false);
|
|
// update style
|
var includeStyles = [
|
'color', 'borderColor', 'borderWidth', 'opacity',
|
'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'
|
];
|
handle.setStyle(handleModel.getItemStyle(null, includeStyles));
|
|
// update position
|
var handleSize = handleModel.get('size');
|
if (!zrUtil.isArray(handleSize)) {
|
handleSize = [handleSize, handleSize];
|
}
|
handle.attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
|
|
throttle.createOrUpdate(
|
this,
|
'_doDispatchAxisPointer',
|
handleModel.get('throttle') || 0,
|
'fixRate'
|
);
|
|
this._moveHandleToValue(value, isInit);
|
},
|
|
/**
|
* @private
|
*/
|
_moveHandleToValue: function (value, isInit) {
|
updateProps(
|
this._axisPointerModel,
|
!isInit && this._moveAnimation,
|
this._handle,
|
getHandleTransProps(this.getHandleTransform(
|
value, this._axisModel, this._axisPointerModel
|
))
|
);
|
},
|
|
/**
|
* @private
|
*/
|
_onHandleDragMove: function (dx, dy) {
|
var handle = this._handle;
|
if (!handle) {
|
return;
|
}
|
|
this._dragging = true;
|
|
// Persistent for throttle.
|
var trans = this.updateHandleTransform(
|
getHandleTransProps(handle),
|
[dx, dy],
|
this._axisModel,
|
this._axisPointerModel
|
);
|
this._payloadInfo = trans;
|
|
handle.stopAnimation();
|
handle.attr(getHandleTransProps(trans));
|
get(handle).lastProp = null;
|
|
this._doDispatchAxisPointer();
|
},
|
|
/**
|
* Throttled method.
|
* @private
|
*/
|
_doDispatchAxisPointer: function () {
|
var handle = this._handle;
|
if (!handle) {
|
return;
|
}
|
|
var payloadInfo = this._payloadInfo;
|
var payload = {
|
type: 'updateAxisPointer',
|
x: payloadInfo.cursorPoint[0],
|
y: payloadInfo.cursorPoint[1],
|
tooltipOption: payloadInfo.tooltipOption,
|
highDownKey: 'axisPointerHandle'
|
};
|
var axis = this._axisModel.axis;
|
payload[axis.dim + 'AxisId'] = this._axisModel.id;
|
this._api.dispatchAction(payload);
|
},
|
|
/**
|
* @private
|
*/
|
_onHandleDragEnd: function (moveAnimation) {
|
this._dragging = false;
|
var handle = this._handle;
|
if (!handle) {
|
return;
|
}
|
|
var value = this._axisPointerModel.get('value');
|
// Consider snap or categroy axis, handle may be not consistent with
|
// axisPointer. So move handle to align the exact value position when
|
// drag ended.
|
this._moveHandleToValue(value);
|
|
// For the effect: tooltip will be shown when finger holding on handle
|
// button, and will be hidden after finger left handle button.
|
this._api.dispatchAction({
|
type: 'hideTip'
|
});
|
},
|
|
/**
|
* Should be implemenented by sub-class if support `handle`.
|
* @protected
|
* @param {number} value
|
* @param {module:echarts/model/Model} axisModel
|
* @param {module:echarts/model/Model} axisPointerModel
|
* @return {Object} {position: [x, y], rotation: 0}
|
*/
|
getHandleTransform: null,
|
|
/**
|
* * Should be implemenented by sub-class if support `handle`.
|
* @protected
|
* @param {Object} transform {position, rotation}
|
* @param {Array.<number>} delta [dx, dy]
|
* @param {module:echarts/model/Model} axisModel
|
* @param {module:echarts/model/Model} axisPointerModel
|
* @return {Object} {position: [x, y], rotation: 0, cursorPoint: [x, y]}
|
*/
|
updateHandleTransform: null,
|
|
/**
|
* @private
|
*/
|
clear: function (api) {
|
this._lastValue = null;
|
this._lastStatus = null;
|
|
var zr = api.getZr();
|
var group = this._group;
|
var handle = this._handle;
|
if (zr && group) {
|
this._lastGraphicKey = null;
|
group && zr.remove(group);
|
handle && zr.remove(handle);
|
this._group = null;
|
this._handle = null;
|
this._payloadInfo = null;
|
}
|
},
|
|
/**
|
* @protected
|
*/
|
doClear: function () {
|
// Implemented by sub-class if necessary.
|
},
|
|
/**
|
* @protected
|
* @param {Array.<number>} xy
|
* @param {Array.<number>} wh
|
* @param {number} [xDimIndex=0] or 1
|
*/
|
buildLabel: function (xy, wh, xDimIndex) {
|
xDimIndex = xDimIndex || 0;
|
return {
|
x: xy[xDimIndex],
|
y: xy[1 - xDimIndex],
|
width: wh[xDimIndex],
|
height: wh[1 - xDimIndex]
|
};
|
}
|
};
|
|
BaseAxisPointer.prototype.constructor = BaseAxisPointer;
|
|
|
function updateProps(animationModel, moveAnimation, el, props) {
|
// Animation optimize.
|
if (!propsEqual(get(el).lastProp, props)) {
|
get(el).lastProp = props;
|
moveAnimation
|
? graphic.updateProps(el, props, animationModel)
|
: (el.stopAnimation(), el.attr(props));
|
}
|
}
|
|
function propsEqual(lastProps, newProps) {
|
if (zrUtil.isObject(lastProps) && zrUtil.isObject(newProps)) {
|
var equals = true;
|
zrUtil.each(newProps, function (item, key) {
|
equals &= propsEqual(lastProps[key], item);
|
});
|
return !!equals;
|
}
|
else {
|
return lastProps === newProps;
|
}
|
}
|
|
function updateLabelShowHide(labelEl, axisPointerModel) {
|
labelEl[axisPointerModel.get('label.show') ? 'show' : 'hide']();
|
}
|
|
function getHandleTransProps(trans) {
|
return {
|
position: trans.position.slice(),
|
rotation: trans.rotation || 0
|
};
|
}
|
|
function createIcon(handleModel, handlers) {
|
var iconStr = handleModel.get('icon');
|
var style = {
|
x: -1, y: -1, width: 2, height: 2
|
};
|
var opt = zrUtil.extend({
|
style: {
|
strokeNoScale: true
|
},
|
rectHover: true,
|
cursor: 'move',
|
draggable: true
|
}, handlers);
|
|
return iconStr.indexOf('image://') === 0
|
? (
|
style.image = iconStr.slice(8),
|
opt.style = style,
|
new graphic.Image(opt)
|
)
|
: graphic.makePath(
|
iconStr.replace('path://', ''),
|
opt,
|
style,
|
'center'
|
);
|
}
|
|
function updateMandatoryProps(group, axisPointerModel, silent) {
|
var z = axisPointerModel.get('z');
|
var zlevel = axisPointerModel.get('zlevel');
|
|
group && group.traverse(function (el) {
|
if (el.type !== 'group') {
|
z != null && (el.z = z);
|
zlevel != null && (el.zlevel = zlevel);
|
el.silent = silent;
|
}
|
});
|
}
|
|
clazzUtil.enableClassExtend(BaseAxisPointer);
|
|
module.exports = BaseAxisPointer;
|
|
|
/***/ },
|
/* 305 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var textContain = __webpack_require__(8);
|
var formatUtil = __webpack_require__(6);
|
var matrix = __webpack_require__(11);
|
var axisHelper = __webpack_require__(105);
|
var AxisBuilder = __webpack_require__(135);
|
|
var helper = {};
|
|
/**
|
* @param {module:echarts/model/Model} axisPointerModel
|
*/
|
helper.buildElStyle = function (axisPointerModel) {
|
var axisPointerType = axisPointerModel.get('type');
|
var styleModel = axisPointerModel.getModel(axisPointerType + 'Style');
|
var style;
|
if (axisPointerType === 'line') {
|
style = styleModel.getLineStyle();
|
style.fill = null;
|
}
|
else if (axisPointerType === 'shadow') {
|
style = styleModel.getAreaStyle();
|
style.stroke = null;
|
}
|
return style;
|
};
|
|
/**
|
* @param {Function} labelPos {align, verticalAlign, position}
|
*/
|
helper.buildLabelElOption = function (
|
elOption, axisModel, axisPointerModel, api, labelPos
|
) {
|
var value = axisPointerModel.get('value');
|
var text = helper.getValueLabel(
|
value, axisModel.axis, axisModel.ecModel,
|
axisPointerModel.get('seriesDataIndices'),
|
{
|
precision: axisPointerModel.get('label.precision'),
|
formatter: axisPointerModel.get('label.formatter')
|
}
|
);
|
var labelModel = axisPointerModel.getModel('label');
|
var textStyleModel = labelModel.getModel('textStyle');
|
var paddings = formatUtil.normalizeCssArray(labelModel.get('padding') || 0);
|
|
var font = textStyleModel.getFont();
|
var textRect = textContain.getBoundingRect(
|
text, font, labelPos.textAlign, labelPos.textBaseline
|
);
|
|
var position = labelPos.position;
|
var width = textRect.width + paddings[1] + paddings[3];
|
var height = textRect.height + paddings[0] + paddings[2];
|
|
// Adjust by align.
|
var align = labelPos.align;
|
align === 'right' && (position[0] -= width);
|
align === 'center' && (position[0] -= width / 2);
|
var verticalAlign = labelPos.verticalAlign;
|
verticalAlign === 'bottom' && (position[1] -= height);
|
verticalAlign === 'middle' && (position[1] -= height / 2);
|
|
// Not overflow ec container
|
confineInContainer(position, width, height, api);
|
|
var bgColor = labelModel.get('backgroundColor');
|
if (!bgColor || bgColor === 'auto') {
|
bgColor = axisModel.get('axisLine.lineStyle.color');
|
}
|
|
elOption.label = {
|
shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},
|
position: position.slice(),
|
style: {
|
text: text,
|
textFont: font,
|
textFill: textStyleModel.getTextColor(),
|
textPosition: 'inside',
|
fill: bgColor,
|
stroke: labelModel.get('borderColor') || 'transparent',
|
lineWidth: labelModel.get('borderWidth') || 0,
|
shadowBlur: labelModel.get('shadowBlur'),
|
shadowColor: labelModel.get('shadowColor'),
|
shadowOffsetX: labelModel.get('shadowOffsetX'),
|
shadowOffsetY: labelModel.get('shadowOffsetY')
|
},
|
// Lable should be over axisPointer.
|
z2: 10
|
};
|
};
|
|
// Do not overflow ec container
|
function confineInContainer(position, width, height, api) {
|
var viewWidth = api.getWidth();
|
var viewHeight = api.getHeight();
|
position[0] = Math.min(position[0] + width, viewWidth) - width;
|
position[1] = Math.min(position[1] + height, viewHeight) - height;
|
position[0] = Math.max(position[0], 0);
|
position[1] = Math.max(position[1], 0);
|
}
|
|
/**
|
* @param {number} value
|
* @param {module:echarts/coord/Axis} axis
|
* @param {module:echarts/model/Global} ecModel
|
* @param {Object} opt
|
* @param {Array.<Object>} seriesDataIndices
|
* @param {number|string} opt.precision 'auto' or a number
|
* @param {string|Function} opt.formatter label formatter
|
*/
|
helper.getValueLabel = function (value, axis, ecModel, seriesDataIndices, opt) {
|
var text = axis.scale.getLabel(
|
// If `precision` is set, width can be fixed (like '12.00500'), which
|
// helps to debounce when when moving label.
|
value, {precision: opt.precision}
|
);
|
var formatter = opt.formatter;
|
|
if (formatter) {
|
var params = {
|
value: axisHelper.getAxisRawValue(axis, value),
|
seriesData: []
|
};
|
zrUtil.each(seriesDataIndices, function (idxItem) {
|
var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
|
var dataIndex = idxItem.dataIndexInside;
|
var dataParams = series && series.getDataParams(dataIndex);
|
dataParams && params.seriesData.push(dataParams);
|
});
|
|
if (zrUtil.isString(formatter)) {
|
text = formatter.replace('{value}', text);
|
}
|
else if (zrUtil.isFunction(formatter)) {
|
text = formatter(params);
|
}
|
}
|
|
return text;
|
};
|
|
/**
|
* @param {module:echarts/coord/Axis} axis
|
* @param {number} value
|
* @param {Object} layoutInfo {
|
* rotation, position, labelOffset, labelDirection, labelMargin
|
* }
|
*/
|
helper.getTransformedPosition = function (axis, value, layoutInfo) {
|
var transform = matrix.create();
|
matrix.rotate(transform, transform, layoutInfo.rotation);
|
matrix.translate(transform, transform, layoutInfo.position);
|
|
return graphic.applyTransform([
|
axis.dataToCoord(value),
|
(layoutInfo.labelOffset || 0)
|
+ (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)
|
], transform);
|
};
|
|
helper.buildCartesianSingleLabelElOption = function (
|
value, elOption, layoutInfo, axisModel, axisPointerModel, api
|
) {
|
var textLayout = AxisBuilder.innerTextLayout(
|
layoutInfo.rotation, 0, layoutInfo.labelDirection
|
);
|
layoutInfo.labelMargin = axisPointerModel.get('label.margin');
|
helper.buildLabelElOption(elOption, axisModel, axisPointerModel, api, {
|
position: helper.getTransformedPosition(axisModel.axis, value, layoutInfo),
|
align: textLayout.textAlign,
|
verticalAlign: textLayout.textVerticalAlign
|
});
|
};
|
|
/**
|
* @param {Array.<number>} p1
|
* @param {Array.<number>} p2
|
* @param {number} [xDimIndex=0] or 1
|
*/
|
helper.makeLineShape = function (p1, p2, xDimIndex) {
|
xDimIndex = xDimIndex || 0;
|
return {
|
x1: p1[xDimIndex],
|
y1: p1[1 - xDimIndex],
|
x2: p2[xDimIndex],
|
y2: p2[1 - xDimIndex]
|
};
|
};
|
|
/**
|
* @param {Array.<number>} xy
|
* @param {Array.<number>} wh
|
* @param {number} [xDimIndex=0] or 1
|
*/
|
helper.makeRectShape = function (xy, wh, xDimIndex) {
|
xDimIndex = xDimIndex || 0;
|
return {
|
x: xy[xDimIndex],
|
y: xy[1 - xDimIndex],
|
width: wh[xDimIndex],
|
height: wh[1 - xDimIndex]
|
};
|
};
|
|
helper.makeSectorShape = function (cx, cy, r0, r, startAngle, endAngle) {
|
return {
|
cx: cx,
|
cy: cy,
|
r0: r0,
|
r: r,
|
startAngle: startAngle,
|
endAngle: endAngle,
|
clockwise: true
|
};
|
};
|
|
module.exports = helper;
|
|
|
/***/ },
|
/* 306 */,
|
/* 307 */,
|
/* 308 */,
|
/* 309 */
|
/***/ function(module, exports) {
|
|
// shim for using process in browser
|
var process = module.exports = {};
|
|
// cached from whatever global is present so that test runners that stub it
|
// don't break things. But we need to wrap it in a try catch in case it is
|
// wrapped in strict mode code which doesn't define any globals. It's inside a
|
// function because try/catches deoptimize in certain engines.
|
|
var cachedSetTimeout;
|
var cachedClearTimeout;
|
|
function defaultSetTimout() {
|
throw new Error('setTimeout has not been defined');
|
}
|
function defaultClearTimeout () {
|
throw new Error('clearTimeout has not been defined');
|
}
|
(function () {
|
try {
|
if (typeof setTimeout === 'function') {
|
cachedSetTimeout = setTimeout;
|
} else {
|
cachedSetTimeout = defaultSetTimout;
|
}
|
} catch (e) {
|
cachedSetTimeout = defaultSetTimout;
|
}
|
try {
|
if (typeof clearTimeout === 'function') {
|
cachedClearTimeout = clearTimeout;
|
} else {
|
cachedClearTimeout = defaultClearTimeout;
|
}
|
} catch (e) {
|
cachedClearTimeout = defaultClearTimeout;
|
}
|
} ())
|
function runTimeout(fun) {
|
if (cachedSetTimeout === setTimeout) {
|
//normal enviroments in sane situations
|
return setTimeout(fun, 0);
|
}
|
// if setTimeout wasn't available but was latter defined
|
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
cachedSetTimeout = setTimeout;
|
return setTimeout(fun, 0);
|
}
|
try {
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
return cachedSetTimeout(fun, 0);
|
} catch(e){
|
try {
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
return cachedSetTimeout.call(null, fun, 0);
|
} catch(e){
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
return cachedSetTimeout.call(this, fun, 0);
|
}
|
}
|
|
|
}
|
function runClearTimeout(marker) {
|
if (cachedClearTimeout === clearTimeout) {
|
//normal enviroments in sane situations
|
return clearTimeout(marker);
|
}
|
// if clearTimeout wasn't available but was latter defined
|
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
cachedClearTimeout = clearTimeout;
|
return clearTimeout(marker);
|
}
|
try {
|
// when when somebody has screwed with setTimeout but no I.E. maddness
|
return cachedClearTimeout(marker);
|
} catch (e){
|
try {
|
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
return cachedClearTimeout.call(null, marker);
|
} catch (e){
|
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
return cachedClearTimeout.call(this, marker);
|
}
|
}
|
|
|
|
}
|
var queue = [];
|
var draining = false;
|
var currentQueue;
|
var queueIndex = -1;
|
|
function cleanUpNextTick() {
|
if (!draining || !currentQueue) {
|
return;
|
}
|
draining = false;
|
if (currentQueue.length) {
|
queue = currentQueue.concat(queue);
|
} else {
|
queueIndex = -1;
|
}
|
if (queue.length) {
|
drainQueue();
|
}
|
}
|
|
function drainQueue() {
|
if (draining) {
|
return;
|
}
|
var timeout = runTimeout(cleanUpNextTick);
|
draining = true;
|
|
var len = queue.length;
|
while(len) {
|
currentQueue = queue;
|
queue = [];
|
while (++queueIndex < len) {
|
if (currentQueue) {
|
currentQueue[queueIndex].run();
|
}
|
}
|
queueIndex = -1;
|
len = queue.length;
|
}
|
currentQueue = null;
|
draining = false;
|
runClearTimeout(timeout);
|
}
|
|
process.nextTick = function (fun) {
|
var args = new Array(arguments.length - 1);
|
if (arguments.length > 1) {
|
for (var i = 1; i < arguments.length; i++) {
|
args[i - 1] = arguments[i];
|
}
|
}
|
queue.push(new Item(fun, args));
|
if (queue.length === 1 && !draining) {
|
runTimeout(drainQueue);
|
}
|
};
|
|
// v8 likes predictible objects
|
function Item(fun, array) {
|
this.fun = fun;
|
this.array = array;
|
}
|
Item.prototype.run = function () {
|
this.fun.apply(null, this.array);
|
};
|
process.title = 'browser';
|
process.browser = true;
|
process.env = {};
|
process.argv = [];
|
process.version = ''; // empty string to avoid regexp issues
|
process.versions = {};
|
|
function noop() {}
|
|
process.on = noop;
|
process.addListener = noop;
|
process.once = noop;
|
process.off = noop;
|
process.removeListener = noop;
|
process.removeAllListeners = noop;
|
process.emit = noop;
|
|
process.binding = function (name) {
|
throw new Error('process.binding is not supported');
|
};
|
|
process.cwd = function () { return '/' };
|
process.chdir = function (dir) {
|
throw new Error('process.chdir is not supported');
|
};
|
process.umask = function() { return 0; };
|
|
|
/***/ },
|
/* 310 */,
|
/* 311 */,
|
/* 312 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var echarts = __webpack_require__(1);
|
var zrUtil = __webpack_require__(4);
|
var modelUtil = __webpack_require__(5);
|
var graphicUtil = __webpack_require__(44);
|
var layoutUtil = __webpack_require__(21);
|
|
// -------------
|
// Preprocessor
|
// -------------
|
|
echarts.registerPreprocessor(function (option) {
|
var graphicOption = option.graphic;
|
|
// Convert
|
// {graphic: [{left: 10, type: 'circle'}, ...]}
|
// or
|
// {graphic: {left: 10, type: 'circle'}}
|
// to
|
// {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]}
|
if (zrUtil.isArray(graphicOption)) {
|
if (!graphicOption[0] || !graphicOption[0].elements) {
|
option.graphic = [{elements: graphicOption}];
|
}
|
else {
|
// Only one graphic instance can be instantiated. (We dont
|
// want that too many views are created in echarts._viewMap)
|
option.graphic = [option.graphic[0]];
|
}
|
}
|
else if (graphicOption && !graphicOption.elements) {
|
option.graphic = [{elements: [graphicOption]}];
|
}
|
});
|
|
// ------
|
// Model
|
// ------
|
|
var GraphicModel = echarts.extendComponentModel({
|
|
type: 'graphic',
|
|
defaultOption: {
|
|
// Extra properties for each elements:
|
//
|
// left/right/top/bottom: (like 12, '22%', 'center', default undefined)
|
// If left/rigth is set, shape.x/shape.cx/position will not be used.
|
// If top/bottom is set, shape.y/shape.cy/position will not be used.
|
// This mechanism is useful when you want to position a group/element
|
// against the right side or the center of this container.
|
//
|
// width/height: (can only be pixel value, default 0)
|
// Only be used to specify contianer(group) size, if needed. And
|
// can not be percentage value (like '33%'). See the reason in the
|
// layout algorithm below.
|
//
|
// bounding: (enum: 'all' (default) | 'raw')
|
// Specify how to calculate boundingRect when locating.
|
// 'all': Get uioned and transformed boundingRect
|
// from both itself and its descendants.
|
// This mode simplies confining a group of elements in the bounding
|
// of their ancester container (e.g., using 'right: 0').
|
// 'raw': Only use the boundingRect of itself and before transformed.
|
// This mode is similar to css behavior, which is useful when you
|
// want an element to be able to overflow its container. (Consider
|
// a rotated circle needs to be located in a corner.)
|
|
// Note: elements is always behind its ancestors in this elements array.
|
elements: [],
|
parentId: null
|
},
|
|
/**
|
* Save el options for the sake of the performance (only update modified graphics).
|
* The order is the same as those in option. (ancesters -> descendants)
|
*
|
* @private
|
* @type {Array.<Object>}
|
*/
|
_elOptionsToUpdate: null,
|
|
/**
|
* @override
|
*/
|
mergeOption: function (option) {
|
// Prevent default merge to elements
|
var elements = this.option.elements;
|
this.option.elements = null;
|
|
GraphicModel.superApply(this, 'mergeOption', arguments);
|
|
this.option.elements = elements;
|
},
|
|
/**
|
* @override
|
*/
|
optionUpdated: function (newOption, isInit) {
|
var thisOption = this.option;
|
var newList = (isInit ? thisOption : newOption).elements;
|
var existList = thisOption.elements = isInit ? [] : thisOption.elements;
|
|
var flattenedList = [];
|
this._flatten(newList, flattenedList);
|
|
var mappingResult = modelUtil.mappingToExists(existList, flattenedList);
|
modelUtil.makeIdAndName(mappingResult);
|
|
// Clear elOptionsToUpdate
|
var elOptionsToUpdate = this._elOptionsToUpdate = [];
|
|
zrUtil.each(mappingResult, function (resultItem, index) {
|
var newElOption = resultItem.option;
|
|
if (true) {
|
zrUtil.assert(
|
zrUtil.isObject(newElOption) || resultItem.exist,
|
'Empty graphic option definition'
|
);
|
}
|
|
if (!newElOption) {
|
return;
|
}
|
|
elOptionsToUpdate.push(newElOption);
|
|
setKeyInfoToNewElOption(resultItem, newElOption);
|
|
mergeNewElOptionToExist(existList, index, newElOption);
|
|
setLayoutInfoToExist(existList[index], newElOption);
|
|
}, this);
|
|
// Clean
|
for (var i = existList.length - 1; i >= 0; i--) {
|
if (existList[i] == null) {
|
existList.splice(i, 1);
|
}
|
else {
|
// $action should be volatile, otherwise option gotten from
|
// `getOption` will contain unexpected $action.
|
delete existList[i].$action;
|
}
|
}
|
},
|
|
/**
|
* Convert
|
* [{
|
* type: 'group',
|
* id: 'xx',
|
* children: [{type: 'circle'}, {type: 'polygon'}]
|
* }]
|
* to
|
* [
|
* {type: 'group', id: 'xx'},
|
* {type: 'circle', parentId: 'xx'},
|
* {type: 'polygon', parentId: 'xx'}
|
* ]
|
*
|
* @private
|
* @param {Array.<Object>} optionList option list
|
* @param {Array.<Object>} result result of flatten
|
* @param {Object} parentOption parent option
|
*/
|
_flatten: function (optionList, result, parentOption) {
|
zrUtil.each(optionList, function (option) {
|
if (!option) {
|
return;
|
}
|
|
if (parentOption) {
|
option.parentOption = parentOption;
|
}
|
|
result.push(option);
|
|
var children = option.children;
|
if (option.type === 'group' && children) {
|
this._flatten(children, result, option);
|
}
|
// Deleting for JSON output, and for not affecting group creation.
|
delete option.children;
|
}, this);
|
},
|
|
// FIXME
|
// Pass to view using payload? setOption has a payload?
|
useElOptionsToUpdate: function () {
|
var els = this._elOptionsToUpdate;
|
// Clear to avoid render duplicately when zooming.
|
this._elOptionsToUpdate = null;
|
return els;
|
}
|
});
|
|
// -----
|
// View
|
// -----
|
|
echarts.extendComponentView({
|
|
type: 'graphic',
|
|
/**
|
* @override
|
*/
|
init: function (ecModel, api) {
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._elMap = {};
|
|
/**
|
* @private
|
* @type {module:echarts/graphic/GraphicModel}
|
*/
|
this._lastGraphicModel;
|
},
|
|
/**
|
* @override
|
*/
|
render: function (graphicModel, ecModel, api) {
|
|
// Having leveraged between use cases and algorithm complexity, a very
|
// simple layout mechanism is used:
|
// The size(width/height) can be determined by itself or its parent (not
|
// implemented yet), but can not by its children. (Top-down travel)
|
// The location(x/y) can be determined by the bounding rect of itself
|
// (can including its descendants or not) and the size of its parent.
|
// (Bottom-up travel)
|
|
// When `chart.clear()` or `chart.setOption({...}, true)` with the same id,
|
// view will be reused.
|
if (graphicModel !== this._lastGraphicModel) {
|
this._clear();
|
}
|
this._lastGraphicModel = graphicModel;
|
|
this._updateElements(graphicModel, api);
|
this._relocate(graphicModel, api);
|
},
|
|
/**
|
* Update graphic elements.
|
*
|
* @private
|
* @param {Object} graphicModel graphic model
|
* @param {module:echarts/ExtensionAPI} api extension API
|
*/
|
_updateElements: function (graphicModel, api) {
|
var elOptionsToUpdate = graphicModel.useElOptionsToUpdate();
|
|
if (!elOptionsToUpdate) {
|
return;
|
}
|
|
var elMap = this._elMap;
|
var rootGroup = this.group;
|
|
// Top-down tranverse to assign graphic settings to each elements.
|
zrUtil.each(elOptionsToUpdate, function (elOption) {
|
var $action = elOption.$action;
|
var id = elOption.id;
|
var existEl = elMap[id];
|
var parentId = elOption.parentId;
|
var targetElParent = parentId != null ? elMap[parentId] : rootGroup;
|
|
// In top/bottom mode, textVertical should not be used. And textBaseline
|
// should not be 'alphabetic', which cause inaccurately locating.
|
if (elOption.hv && elOption.hv[1] && elOption.type === 'text') {
|
elOption.style = zrUtil.defaults({textBaseline: 'middle'}, elOption.style);
|
elOption.style.textVerticalAlign = null;
|
}
|
|
// Remove unnecessary props to avoid potential problems.
|
var elOptionCleaned = getCleanedElOption(elOption);
|
|
// For simple, do not support parent change, otherwise reorder is needed.
|
if (true) {
|
existEl && zrUtil.assert(
|
targetElParent === existEl.parent,
|
'Changing parent is not supported.'
|
);
|
}
|
|
if (!$action || $action === 'merge') {
|
existEl
|
? existEl.attr(elOptionCleaned)
|
: createEl(id, targetElParent, elOptionCleaned, elMap);
|
}
|
else if ($action === 'replace') {
|
removeEl(existEl, elMap);
|
createEl(id, targetElParent, elOptionCleaned, elMap);
|
}
|
else if ($action === 'remove') {
|
removeEl(existEl, elMap);
|
}
|
|
if (elMap[id]) {
|
elMap[id].__ecGraphicWidth = elOption.width;
|
elMap[id].__ecGraphicHeight = elOption.height;
|
}
|
});
|
},
|
|
/**
|
* Locate graphic elements.
|
*
|
* @private
|
* @param {Object} graphicModel graphic model
|
* @param {module:echarts/ExtensionAPI} api extension API
|
*/
|
_relocate: function (graphicModel, api) {
|
var elOptions = graphicModel.option.elements;
|
var rootGroup = this.group;
|
var elMap = this._elMap;
|
|
// Bottom-up tranvese all elements (consider ec resize) to locate elements.
|
for (var i = elOptions.length - 1; i >= 0; i--) {
|
var elOption = elOptions[i];
|
var el = elMap[elOption.id];
|
|
if (!el) {
|
continue;
|
}
|
|
var parentEl = el.parent;
|
var containerInfo = parentEl === rootGroup
|
? {
|
width: api.getWidth(),
|
height: api.getHeight()
|
}
|
: { // Like 'position:absolut' in css, default 0.
|
width: parentEl.__ecGraphicWidth || 0,
|
height: parentEl.__ecGraphicHeight || 0
|
};
|
|
layoutUtil.positionElement(
|
el, elOption, containerInfo, null,
|
{hv: elOption.hv, boundingMode: elOption.bounding}
|
);
|
}
|
},
|
|
/**
|
* Clear all elements.
|
*
|
* @private
|
*/
|
_clear: function () {
|
var elMap = this._elMap;
|
zrUtil.each(elMap, function (el) {
|
removeEl(el, elMap);
|
});
|
this._elMap = {};
|
},
|
|
/**
|
* @override
|
*/
|
dispose: function () {
|
this._clear();
|
}
|
});
|
|
function createEl(id, targetElParent, elOption, elMap) {
|
var graphicType = elOption.type;
|
|
if (true) {
|
zrUtil.assert(graphicType, 'graphic type MUST be set');
|
}
|
|
var Clz = graphicUtil[graphicType.charAt(0).toUpperCase() + graphicType.slice(1)];
|
|
if (true) {
|
zrUtil.assert(Clz, 'graphic type can not be found');
|
}
|
|
var el = new Clz(elOption);
|
targetElParent.add(el);
|
elMap[id] = el;
|
el.__ecGraphicId = id;
|
}
|
|
function removeEl(existEl, elMap) {
|
var existElParent = existEl && existEl.parent;
|
if (existElParent) {
|
existEl.type === 'group' && existEl.traverse(function (el) {
|
removeEl(el, elMap);
|
});
|
delete elMap[existEl.__ecGraphicId];
|
existElParent.remove(existEl);
|
}
|
}
|
|
// Remove unnecessary props to avoid potential problems.
|
function getCleanedElOption(elOption) {
|
elOption = zrUtil.extend({}, elOption);
|
zrUtil.each(
|
['id', 'parentId', '$action', 'hv', 'bounding'].concat(layoutUtil.LOCATION_PARAMS),
|
function (name) {
|
delete elOption[name];
|
}
|
);
|
return elOption;
|
}
|
|
function isSetLoc(obj, props) {
|
var isSet;
|
zrUtil.each(props, function (prop) {
|
obj[prop] != null && obj[prop] !== 'auto' && (isSet = true);
|
});
|
return isSet;
|
}
|
|
function setKeyInfoToNewElOption(resultItem, newElOption) {
|
var existElOption = resultItem.exist;
|
|
// Set id and type after id assigned.
|
newElOption.id = resultItem.keyInfo.id;
|
!newElOption.type && existElOption && (newElOption.type = existElOption.type);
|
|
// Set parent id if not specified
|
if (newElOption.parentId == null) {
|
var newElParentOption = newElOption.parentOption;
|
if (newElParentOption) {
|
newElOption.parentId = newElParentOption.id;
|
}
|
else if (existElOption) {
|
newElOption.parentId = existElOption.parentId;
|
}
|
}
|
|
// Clear
|
newElOption.parentOption = null;
|
}
|
|
function mergeNewElOptionToExist(existList, index, newElOption) {
|
// Update existing options, for `getOption` feature.
|
var newElOptCopy = zrUtil.extend({}, newElOption);
|
var existElOption = existList[index];
|
|
var $action = newElOption.$action || 'merge';
|
if ($action === 'merge') {
|
if (existElOption) {
|
|
if (true) {
|
var newType = newElOption.type;
|
zrUtil.assert(
|
!newType || existElOption.type === newType,
|
'Please set $action: "replace" to change `type`'
|
);
|
}
|
|
// We can ensure that newElOptCopy and existElOption are not
|
// the same object, so `merge` will not change newElOptCopy.
|
zrUtil.merge(existElOption, newElOptCopy, true);
|
// Rigid body, use ignoreSize.
|
layoutUtil.mergeLayoutParam(existElOption, newElOptCopy, {ignoreSize: true});
|
// Will be used in render.
|
layoutUtil.copyLayoutParams(newElOption, existElOption);
|
}
|
else {
|
existList[index] = newElOptCopy;
|
}
|
}
|
else if ($action === 'replace') {
|
existList[index] = newElOptCopy;
|
}
|
else if ($action === 'remove') {
|
// null will be cleaned later.
|
existElOption && (existList[index] = null);
|
}
|
}
|
|
function setLayoutInfoToExist(existItem, newElOption) {
|
if (!existItem) {
|
return;
|
}
|
existItem.hv = newElOption.hv = [
|
// Rigid body, dont care `width`.
|
isSetLoc(newElOption, ['left', 'right']),
|
// Rigid body, dont care `height`.
|
isSetLoc(newElOption, ['top', 'bottom'])
|
];
|
// Give default group size. Otherwise layout error may occur.
|
if (existItem.type === 'group') {
|
existItem.width == null && (existItem.width = newElOption.width = 0);
|
existItem.height == null && (existItem.height = newElOption.height = 0);
|
}
|
}
|
|
|
|
/***/ },
|
/* 313 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(123);
|
|
__webpack_require__(303);
|
|
__webpack_require__(297);
|
|
|
|
/***/ },
|
/* 314 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* Legend component entry file8
|
*/
|
|
|
__webpack_require__(315);
|
__webpack_require__(316);
|
__webpack_require__(317);
|
|
var echarts = __webpack_require__(1);
|
// Series Filter
|
echarts.registerProcessor(__webpack_require__(319));
|
|
|
/***/ },
|
/* 315 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var Model = __webpack_require__(12);
|
|
var LegendModel = __webpack_require__(1).extendComponentModel({
|
|
type: 'legend',
|
|
dependencies: ['series'],
|
|
layoutMode: {
|
type: 'box',
|
ignoreSize: true
|
},
|
|
init: function (option, parentModel, ecModel) {
|
this.mergeDefaultAndTheme(option, ecModel);
|
|
option.selected = option.selected || {};
|
},
|
|
mergeOption: function (option) {
|
LegendModel.superCall(this, 'mergeOption', option);
|
},
|
|
optionUpdated: function () {
|
this._updateData(this.ecModel);
|
|
var legendData = this._data;
|
|
// If selectedMode is single, try to select one
|
if (legendData[0] && this.get('selectedMode') === 'single') {
|
var hasSelected = false;
|
// If has any selected in option.selected
|
for (var i = 0; i < legendData.length; i++) {
|
var name = legendData[i].get('name');
|
if (this.isSelected(name)) {
|
// Force to unselect others
|
this.select(name);
|
hasSelected = true;
|
break;
|
}
|
}
|
// Try select the first if selectedMode is single
|
!hasSelected && this.select(legendData[0].get('name'));
|
}
|
},
|
|
_updateData: function (ecModel) {
|
var legendData = zrUtil.map(this.get('data') || [], function (dataItem) {
|
// Can be string or number
|
if (typeof dataItem === 'string' || typeof dataItem === 'number') {
|
dataItem = {
|
name: dataItem
|
};
|
}
|
return new Model(dataItem, this, this.ecModel);
|
}, this);
|
this._data = legendData;
|
|
var availableNames = zrUtil.map(ecModel.getSeries(), function (series) {
|
return series.name;
|
});
|
ecModel.eachSeries(function (seriesModel) {
|
if (seriesModel.legendDataProvider) {
|
var data = seriesModel.legendDataProvider();
|
availableNames = availableNames.concat(data.mapArray(data.getName));
|
}
|
});
|
/**
|
* @type {Array.<string>}
|
* @private
|
*/
|
this._availableNames = availableNames;
|
},
|
|
/**
|
* @return {Array.<module:echarts/model/Model>}
|
*/
|
getData: function () {
|
return this._data;
|
},
|
|
/**
|
* @param {string} name
|
*/
|
select: function (name) {
|
var selected = this.option.selected;
|
var selectedMode = this.get('selectedMode');
|
if (selectedMode === 'single') {
|
var data = this._data;
|
zrUtil.each(data, function (dataItem) {
|
selected[dataItem.get('name')] = false;
|
});
|
}
|
selected[name] = true;
|
},
|
|
/**
|
* @param {string} name
|
*/
|
unSelect: function (name) {
|
if (this.get('selectedMode') !== 'single') {
|
this.option.selected[name] = false;
|
}
|
},
|
|
/**
|
* @param {string} name
|
*/
|
toggleSelected: function (name) {
|
var selected = this.option.selected;
|
// Default is true
|
if (!selected.hasOwnProperty(name)) {
|
selected[name] = true;
|
}
|
this[selected[name] ? 'unSelect' : 'select'](name);
|
},
|
|
/**
|
* @param {string} name
|
*/
|
isSelected: function (name) {
|
var selected = this.option.selected;
|
return !(selected.hasOwnProperty(name) && !selected[name])
|
&& zrUtil.indexOf(this._availableNames, name) >= 0;
|
},
|
|
defaultOption: {
|
// 一级层叠
|
zlevel: 0,
|
// 二级层叠
|
z: 4,
|
show: true,
|
|
// 布局方式,默认为水平布局,可选为:
|
// 'horizontal' | 'vertical'
|
orient: 'horizontal',
|
|
left: 'center',
|
// right: 'center',
|
|
top: 'top',
|
// bottom: 'top',
|
|
// 水平对齐
|
// 'auto' | 'left' | 'right'
|
// 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐
|
align: 'auto',
|
|
backgroundColor: 'rgba(0,0,0,0)',
|
// 图例边框颜色
|
borderColor: '#ccc',
|
// 图例边框线宽,单位px,默认为0(无边框)
|
borderWidth: 0,
|
// 图例内边距,单位px,默认各方向内边距为5,
|
// 接受数组分别设定上右下左边距,同css
|
padding: 5,
|
// 各个item之间的间隔,单位px,默认为10,
|
// 横向布局时为水平间隔,纵向布局时为纵向间隔
|
itemGap: 10,
|
// 图例图形宽度
|
itemWidth: 25,
|
// 图例图形高度
|
itemHeight: 14,
|
|
// 图例关闭时候的颜色
|
inactiveColor: '#ccc',
|
|
textStyle: {
|
// 图例文字颜色
|
color: '#333'
|
},
|
// formatter: '',
|
// 选择模式,默认开启图例开关
|
selectedMode: true,
|
// 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入
|
// selected: null,
|
// 图例内容(详见legend.data,数组中每一项代表一个item
|
// data: [],
|
|
// Tooltip 相关配置
|
tooltip: {
|
show: false
|
}
|
}
|
});
|
|
module.exports = LegendModel;
|
|
|
/***/ },
|
/* 316 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Legend action
|
*/
|
|
|
var echarts = __webpack_require__(1);
|
var zrUtil = __webpack_require__(4);
|
|
function legendSelectActionHandler(methodName, payload, ecModel) {
|
var selectedMap = {};
|
var isToggleSelect = methodName === 'toggleSelected';
|
var isSelected;
|
// Update all legend components
|
ecModel.eachComponent('legend', function (legendModel) {
|
if (isToggleSelect && isSelected != null) {
|
// Force other legend has same selected status
|
// Or the first is toggled to true and other are toggled to false
|
// In the case one legend has some item unSelected in option. And if other legend
|
// doesn't has the item, they will assume it is selected.
|
legendModel[isSelected ? 'select' : 'unSelect'](payload.name);
|
}
|
else {
|
legendModel[methodName](payload.name);
|
isSelected = legendModel.isSelected(payload.name);
|
}
|
var legendData = legendModel.getData();
|
zrUtil.each(legendData, function (model) {
|
var name = model.get('name');
|
// Wrap element
|
if (name === '\n' || name === '') {
|
return;
|
}
|
var isItemSelected = legendModel.isSelected(name);
|
if (name in selectedMap) {
|
// Unselected if any legend is unselected
|
selectedMap[name] = selectedMap[name] && isItemSelected;
|
}
|
else {
|
selectedMap[name] = isItemSelected;
|
}
|
});
|
});
|
// Return the event explicitly
|
return {
|
name: payload.name,
|
selected: selectedMap
|
};
|
}
|
/**
|
* @event legendToggleSelect
|
* @type {Object}
|
* @property {string} type 'legendToggleSelect'
|
* @property {string} [from]
|
* @property {string} name Series name or data item name
|
*/
|
echarts.registerAction(
|
'legendToggleSelect', 'legendselectchanged',
|
zrUtil.curry(legendSelectActionHandler, 'toggleSelected')
|
);
|
|
/**
|
* @event legendSelect
|
* @type {Object}
|
* @property {string} type 'legendSelect'
|
* @property {string} name Series name or data item name
|
*/
|
echarts.registerAction(
|
'legendSelect', 'legendselected',
|
zrUtil.curry(legendSelectActionHandler, 'select')
|
);
|
|
/**
|
* @event legendUnSelect
|
* @type {Object}
|
* @property {string} type 'legendUnSelect'
|
* @property {string} name Series name or data item name
|
*/
|
echarts.registerAction(
|
'legendUnSelect', 'legendunselected',
|
zrUtil.curry(legendSelectActionHandler, 'unSelect')
|
);
|
|
|
/***/ },
|
/* 317 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var symbolCreator = __webpack_require__(104);
|
var graphic = __webpack_require__(44);
|
var listComponentHelper = __webpack_require__(318);
|
|
var curry = zrUtil.curry;
|
|
function dispatchSelectAction(name, api) {
|
api.dispatchAction({
|
type: 'legendToggleSelect',
|
name: name
|
});
|
}
|
|
function dispatchHighlightAction(seriesModel, dataName, api) {
|
// If element hover will move to a hoverLayer.
|
var el = api.getZr().storage.getDisplayList()[0];
|
if (!(el && el.useHoverLayer)) {
|
seriesModel.get('legendHoverLink') && api.dispatchAction({
|
type: 'highlight',
|
seriesName: seriesModel.name,
|
name: dataName
|
});
|
}
|
}
|
|
function dispatchDownplayAction(seriesModel, dataName, api) {
|
// If element hover will move to a hoverLayer.
|
var el = api.getZr().storage.getDisplayList()[0];
|
if (!(el && el.useHoverLayer)) {
|
seriesModel.get('legendHoverLink') && api.dispatchAction({
|
type: 'downplay',
|
seriesName: seriesModel.name,
|
name: dataName
|
});
|
}
|
}
|
|
module.exports = __webpack_require__(1).extendComponentView({
|
|
type: 'legend',
|
|
init: function () {
|
this._symbolTypeStore = {};
|
},
|
|
render: function (legendModel, ecModel, api) {
|
var group = this.group;
|
group.removeAll();
|
|
if (!legendModel.get('show')) {
|
return;
|
}
|
|
var selectMode = legendModel.get('selectedMode');
|
var itemAlign = legendModel.get('align');
|
|
if (itemAlign === 'auto') {
|
itemAlign = (legendModel.get('left') === 'right'
|
&& legendModel.get('orient') === 'vertical')
|
? 'right' : 'left';
|
}
|
|
var legendDrawedMap = {};
|
|
zrUtil.each(legendModel.getData(), function (itemModel) {
|
var name = itemModel.get('name');
|
|
// Use empty string or \n as a newline string
|
if (name === '' || name === '\n') {
|
group.add(new graphic.Group({
|
newline: true
|
}));
|
return;
|
}
|
|
var seriesModel = ecModel.getSeriesByName(name)[0];
|
|
if (legendDrawedMap[name]) {
|
// Have been drawed
|
return;
|
}
|
|
// Series legend
|
if (seriesModel) {
|
var data = seriesModel.getData();
|
var color = data.getVisual('color');
|
|
// If color is a callback function
|
if (typeof color === 'function') {
|
// Use the first data
|
color = color(seriesModel.getDataParams(0));
|
}
|
|
// Using rect symbol defaultly
|
var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect';
|
var symbolType = data.getVisual('symbol');
|
|
var itemGroup = this._createItem(
|
name, itemModel, legendModel,
|
legendSymbolType, symbolType,
|
itemAlign, color,
|
selectMode
|
);
|
|
itemGroup.on('click', curry(dispatchSelectAction, name, api))
|
.on('mouseover', curry(dispatchHighlightAction, seriesModel, null, api))
|
.on('mouseout', curry(dispatchDownplayAction, seriesModel, null, api));
|
|
legendDrawedMap[name] = true;
|
}
|
else {
|
// Data legend of pie, funnel
|
ecModel.eachRawSeries(function (seriesModel) {
|
// In case multiple series has same data name
|
if (legendDrawedMap[name]) {
|
return;
|
}
|
if (seriesModel.legendDataProvider) {
|
var data = seriesModel.legendDataProvider();
|
var idx = data.indexOfName(name);
|
if (idx < 0) {
|
return;
|
}
|
|
var color = data.getItemVisual(idx, 'color');
|
|
var legendSymbolType = 'roundRect';
|
|
var itemGroup = this._createItem(
|
name, itemModel, legendModel,
|
legendSymbolType, null,
|
itemAlign, color,
|
selectMode
|
);
|
|
itemGroup.on('click', curry(dispatchSelectAction, name, api))
|
// FIXME Should not specify the series name
|
.on('mouseover', curry(dispatchHighlightAction, seriesModel, name, api))
|
.on('mouseout', curry(dispatchDownplayAction, seriesModel, name, api));
|
|
legendDrawedMap[name] = true;
|
}
|
}, this);
|
}
|
|
if (true) {
|
if (!legendDrawedMap[name]) {
|
console.warn(name + ' series not exists. Legend data should be same with series name or data name.');
|
}
|
}
|
}, this);
|
|
listComponentHelper.layout(group, legendModel, api);
|
// Render background after group is layout
|
// FIXME
|
listComponentHelper.addBackground(group, legendModel);
|
},
|
|
_createItem: function (
|
name, itemModel, legendModel,
|
legendSymbolType, symbolType,
|
itemAlign, color, selectMode
|
) {
|
var itemWidth = legendModel.get('itemWidth');
|
var itemHeight = legendModel.get('itemHeight');
|
var inactiveColor = legendModel.get('inactiveColor');
|
|
var isSelected = legendModel.isSelected(name);
|
var itemGroup = new graphic.Group();
|
|
var textStyleModel = itemModel.getModel('textStyle');
|
|
var itemIcon = itemModel.get('icon');
|
|
var tooltipModel = itemModel.getModel('tooltip');
|
var legendGlobalTooltipModel = tooltipModel.parentModel;
|
|
// Use user given icon first
|
legendSymbolType = itemIcon || legendSymbolType;
|
itemGroup.add(symbolCreator.createSymbol(
|
legendSymbolType, 0, 0, itemWidth, itemHeight, isSelected ? color : inactiveColor
|
));
|
|
// Compose symbols
|
// PENDING
|
if (!itemIcon && symbolType
|
// At least show one symbol, can't be all none
|
&& ((symbolType !== legendSymbolType) || symbolType == 'none')
|
) {
|
var size = itemHeight * 0.8;
|
if (symbolType === 'none') {
|
symbolType = 'circle';
|
}
|
// Put symbol in the center
|
itemGroup.add(symbolCreator.createSymbol(
|
symbolType, (itemWidth - size) / 2, (itemHeight - size) / 2, size, size,
|
isSelected ? color : inactiveColor
|
));
|
}
|
|
// Text
|
var textX = itemAlign === 'left' ? itemWidth + 5 : -5;
|
var textAlign = itemAlign;
|
|
var formatter = legendModel.get('formatter');
|
var content = name;
|
if (typeof formatter === 'string' && formatter) {
|
content = formatter.replace('{name}', name != null ? name : '');
|
}
|
else if (typeof formatter === 'function') {
|
content = formatter(name);
|
}
|
|
var text = new graphic.Text({
|
style: {
|
text: content,
|
x: textX,
|
y: itemHeight / 2,
|
fill: isSelected ? textStyleModel.getTextColor() : inactiveColor,
|
textFont: textStyleModel.getFont(),
|
textAlign: textAlign,
|
textVerticalAlign: 'middle'
|
}
|
});
|
itemGroup.add(text);
|
|
// Add a invisible rect to increase the area of mouse hover
|
var hitRect = new graphic.Rect({
|
shape: itemGroup.getBoundingRect(),
|
invisible: true,
|
tooltip: tooltipModel.get('show') ? zrUtil.extend({
|
content: name,
|
// Defaul formatter
|
formatter: legendGlobalTooltipModel.get('formatter', true) || function () {
|
return name;
|
},
|
formatterParams: {
|
componentType: 'legend',
|
legendIndex: legendModel.componentIndex,
|
name: name,
|
$vars: ['name']
|
}
|
}, tooltipModel.option) : null
|
});
|
itemGroup.add(hitRect);
|
|
itemGroup.eachChild(function (child) {
|
child.silent = true;
|
});
|
|
hitRect.silent = !selectMode;
|
|
|
|
this.group.add(itemGroup);
|
|
graphic.setHoverStyle(itemGroup);
|
|
return itemGroup;
|
}
|
});
|
|
|
/***/ },
|
/* 318 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
// List layout
|
var layout = __webpack_require__(21);
|
var formatUtil = __webpack_require__(6);
|
var graphic = __webpack_require__(44);
|
|
function positionGroup(group, model, api) {
|
layout.positionElement(
|
group, model.getBoxLayoutParams(),
|
{
|
width: api.getWidth(),
|
height: api.getHeight()
|
},
|
model.get('padding')
|
);
|
}
|
|
module.exports = {
|
/**
|
* Layout list like component.
|
* It will box layout each items in group of component and then position the whole group in the viewport
|
* @param {module:zrender/group/Group} group
|
* @param {module:echarts/model/Component} componentModel
|
* @param {module:echarts/ExtensionAPI}
|
*/
|
layout: function (group, componentModel, api) {
|
var rect = layout.getLayoutRect(componentModel.getBoxLayoutParams(), {
|
width: api.getWidth(),
|
height: api.getHeight()
|
}, componentModel.get('padding'));
|
layout.box(
|
componentModel.get('orient'),
|
group,
|
componentModel.get('itemGap'),
|
rect.width,
|
rect.height
|
);
|
|
positionGroup(group, componentModel, api);
|
},
|
|
addBackground: function (group, componentModel) {
|
var padding = formatUtil.normalizeCssArray(
|
componentModel.get('padding')
|
);
|
var boundingRect = group.getBoundingRect();
|
var style = componentModel.getItemStyle(['color', 'opacity']);
|
style.fill = componentModel.get('backgroundColor');
|
var rect = new graphic.Rect({
|
shape: {
|
x: boundingRect.x - padding[3],
|
y: boundingRect.y - padding[0],
|
width: boundingRect.width + padding[1] + padding[3],
|
height: boundingRect.height + padding[0] + padding[2]
|
},
|
style: style,
|
silent: true,
|
z2: -1
|
});
|
graphic.subPixelOptimizeRect(rect);
|
|
group.add(rect);
|
}
|
};
|
|
|
/***/ },
|
/* 319 */
|
/***/ function(module, exports) {
|
|
|
module.exports = function (ecModel) {
|
var legendModels = ecModel.findComponents({
|
mainType: 'legend'
|
});
|
if (legendModels && legendModels.length) {
|
ecModel.filterSeries(function (series) {
|
// If in any legend component the status is not selected.
|
// Because in legend series is assumed selected when it is not in the legend data.
|
for (var i = 0; i < legendModels.length; i++) {
|
if (!legendModels[i].isSelected(series.name)) {
|
return false;
|
}
|
}
|
return true;
|
});
|
}
|
};
|
|
|
/***/ },
|
/* 320 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// FIXME Better way to pack data in graphic element
|
|
|
__webpack_require__(297);
|
|
__webpack_require__(321);
|
|
__webpack_require__(322);
|
|
|
// Show tip action
|
/**
|
* @action
|
* @property {string} type
|
* @property {number} seriesIndex
|
* @property {number} dataIndex
|
* @property {number} [x]
|
* @property {number} [y]
|
*/
|
__webpack_require__(1).registerAction(
|
{
|
type: 'showTip',
|
event: 'showTip',
|
update: 'tooltip:manuallyShowTip'
|
},
|
// noop
|
function () {}
|
);
|
// Hide tip action
|
__webpack_require__(1).registerAction(
|
{
|
type: 'hideTip',
|
event: 'hideTip',
|
update: 'tooltip:manuallyHideTip'
|
},
|
// noop
|
function () {}
|
);
|
|
|
/***/ },
|
/* 321 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(1).extendComponentModel({
|
|
type: 'tooltip',
|
|
dependencies: ['axisPointer'],
|
|
defaultOption: {
|
zlevel: 0,
|
|
z: 8,
|
|
show: true,
|
|
// tooltip主体内容
|
showContent: true,
|
|
// 'trigger' only works on coordinate system.
|
// 'item' | 'axis' | 'none'
|
trigger: 'item',
|
|
// 'click' | 'mousemove' | 'none'
|
triggerOn: 'mousemove|click',
|
|
alwaysShowContent: false,
|
|
displayMode: 'single', // 'single' | 'multipleByCoordSys'
|
|
// 位置 {Array} | {Function}
|
// position: null
|
// Consider triggered from axisPointer handle, verticalAlign should be 'middle'
|
// align: null,
|
// verticalAlign: null,
|
|
// 是否约束 content 在 viewRect 中。默认 false 是为了兼容以前版本。
|
confine: false,
|
|
// 内容格式器:{string}(Template) ¦ {Function}
|
// formatter: null
|
|
showDelay: 0,
|
|
// 隐藏延迟,单位ms
|
hideDelay: 100,
|
|
// 动画变换时间,单位s
|
transitionDuration: 0.4,
|
|
enterable: false,
|
|
// 提示背景颜色,默认为透明度为0.7的黑色
|
backgroundColor: 'rgba(50,50,50,0.7)',
|
|
// 提示边框颜色
|
borderColor: '#333',
|
|
// 提示边框圆角,单位px,默认为4
|
borderRadius: 4,
|
|
// 提示边框线宽,单位px,默认为0(无边框)
|
borderWidth: 0,
|
|
// 提示内边距,单位px,默认各方向内边距为5,
|
// 接受数组分别设定上右下左边距,同css
|
padding: 5,
|
|
// Extra css text
|
extraCssText: '',
|
|
// 坐标轴指示器,坐标轴触发有效
|
axisPointer: {
|
// 默认为直线
|
// 可选为:'line' | 'shadow' | 'cross'
|
type: 'line',
|
|
// type 为 line 的时候有效,指定 tooltip line 所在的轴,可选
|
// 可选 'x' | 'y' | 'angle' | 'radius' | 'auto'
|
// 默认 'auto',会选择类型为 cateogry 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴
|
// 极坐标系会默认选择 angle 轴
|
axis: 'auto',
|
|
animation: 'auto',
|
animationDurationUpdate: 200,
|
animationEasingUpdate: 'exponentialOut',
|
|
crossStyle: {
|
color: '#999',
|
width: 1,
|
type: 'dashed',
|
|
// TODO formatter
|
textStyle: {}
|
}
|
|
// lineStyle and shadowStyle should not be specified here,
|
// otherwise it will always override those styles on option.axisPointer.
|
},
|
textStyle: {
|
color: '#fff',
|
fontSize: 14
|
}
|
}
|
});
|
|
|
/***/ },
|
/* 322 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var TooltipContent = __webpack_require__(323);
|
var zrUtil = __webpack_require__(4);
|
var formatUtil = __webpack_require__(6);
|
var numberUtil = __webpack_require__(7);
|
var graphic = __webpack_require__(44);
|
var findPointFromSeries = __webpack_require__(299);
|
var layoutUtil = __webpack_require__(21);
|
var env = __webpack_require__(2);
|
var Model = __webpack_require__(12);
|
var globalListener = __webpack_require__(302);
|
var axisHelper = __webpack_require__(105);
|
var axisPointerViewHelper = __webpack_require__(305);
|
|
var bind = zrUtil.bind;
|
var each = zrUtil.each;
|
var parsePercent = numberUtil.parsePercent;
|
|
|
var proxyRect = new graphic.Rect({
|
shape: {x: -1, y: -1, width: 2, height: 2}
|
});
|
|
__webpack_require__(1).extendComponentView({
|
|
type: 'tooltip',
|
|
init: function (ecModel, api) {
|
if (env.node) {
|
return;
|
}
|
var tooltipContent = new TooltipContent(api.getDom(), api);
|
this._tooltipContent = tooltipContent;
|
},
|
|
render: function (tooltipModel, ecModel, api) {
|
if (env.node) {
|
return;
|
}
|
|
// Reset
|
this.group.removeAll();
|
|
/**
|
* @private
|
* @type {module:echarts/component/tooltip/TooltipModel}
|
*/
|
this._tooltipModel = tooltipModel;
|
|
/**
|
* @private
|
* @type {module:echarts/model/Global}
|
*/
|
this._ecModel = ecModel;
|
|
/**
|
* @private
|
* @type {module:echarts/ExtensionAPI}
|
*/
|
this._api = api;
|
|
/**
|
* Should be cleaned when render.
|
* @private
|
* @type {Array.<Array.<Object>>}
|
*/
|
this._lastDataByCoordSys = null;
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
this._alwaysShowContent = tooltipModel.get('alwaysShowContent');
|
|
var tooltipContent = this._tooltipContent;
|
tooltipContent.update();
|
tooltipContent.setEnterable(tooltipModel.get('enterable'));
|
|
this._initGlobalListener();
|
|
this._keepShow();
|
},
|
|
_initGlobalListener: function () {
|
var tooltipModel = this._tooltipModel;
|
var triggerOn = tooltipModel.get('triggerOn');
|
|
globalListener.register(
|
'itemTooltip',
|
this._api,
|
bind(function (currTrigger, e, dispatchAction) {
|
// If 'none', it is not controlled by mouse totally.
|
if (triggerOn !== 'none') {
|
if (triggerOn.indexOf(currTrigger) >= 0) {
|
this._tryShow(e, dispatchAction);
|
}
|
else if (currTrigger === 'leave') {
|
this._hide(dispatchAction);
|
}
|
}
|
}, this)
|
);
|
},
|
|
_keepShow: function () {
|
var tooltipModel = this._tooltipModel;
|
var ecModel = this._ecModel;
|
var api = this._api;
|
|
// Try to keep the tooltip show when refreshing
|
if (this._lastX != null
|
&& this._lastY != null
|
// When user is willing to control tooltip totally using API,
|
// self.manuallyShowTip({x, y}) might cause tooltip hide,
|
// which is not expected.
|
&& tooltipModel.get('triggerOn') !== 'none'
|
) {
|
var self = this;
|
clearTimeout(this._refreshUpdateTimeout);
|
this._refreshUpdateTimeout = setTimeout(function () {
|
// Show tip next tick after other charts are rendered
|
// In case highlight action has wrong result
|
// FIXME
|
self.manuallyShowTip(tooltipModel, ecModel, api, {
|
x: self._lastX,
|
y: self._lastY
|
});
|
});
|
}
|
},
|
|
/**
|
* Show tip manually by
|
* dispatchAction({
|
* type: 'showTip',
|
* x: 10,
|
* y: 10
|
* });
|
* Or
|
* dispatchAction({
|
* type: 'showTip',
|
* seriesIndex: 0,
|
* dataIndex or dataIndexInside or name
|
* });
|
*
|
* TODO Batch
|
*/
|
manuallyShowTip: function (tooltipModel, ecModel, api, payload) {
|
if (payload.from === this.uid || env.node) {
|
return;
|
}
|
|
var dispatchAction = makeDispatchAction(payload, api);
|
|
// Reset ticket
|
this._ticket = '';
|
|
// When triggered from axisPointer.
|
var dataByCoordSys = payload.dataByCoordSys;
|
|
if (payload.tooltip && payload.x != null && payload.y != null) {
|
var el = proxyRect;
|
el.position = [payload.x, payload.y];
|
el.update();
|
el.tooltip = payload.tooltip;
|
// Manually show tooltip while view is not using zrender elements.
|
this._tryShow({
|
offsetX: payload.x,
|
offsetY: payload.y,
|
target: el
|
}, dispatchAction);
|
}
|
else if (dataByCoordSys) {
|
this._tryShow({
|
offsetX: payload.x,
|
offsetY: payload.y,
|
position: payload.position,
|
event: {},
|
dataByCoordSys: payload.dataByCoordSys,
|
tooltipOption: payload.tooltipOption
|
}, dispatchAction);
|
}
|
else if (payload.seriesIndex != null) {
|
|
if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) {
|
return;
|
}
|
|
var pointInfo = findPointFromSeries(payload, ecModel);
|
var cx = pointInfo.point[0];
|
var cy = pointInfo.point[1];
|
if (cx != null && cy != null) {
|
this._tryShow({
|
offsetX: cx,
|
offsetY: cy,
|
position: payload.position,
|
target: pointInfo.el,
|
event: {}
|
}, dispatchAction);
|
}
|
}
|
else if (payload.x != null && payload.y != null) {
|
// FIXME
|
// should wrap dispatchAction like `axisPointer/globalListener` ?
|
api.dispatchAction({
|
type: 'updateAxisPointer',
|
x: payload.x,
|
y: payload.y
|
});
|
|
this._tryShow({
|
offsetX: payload.x,
|
offsetY: payload.y,
|
position: payload.position,
|
target: api.getZr().findHover(payload.x, payload.y).target,
|
event: {}
|
}, dispatchAction);
|
}
|
},
|
|
manuallyHideTip: function (tooltipModel, ecModel, api, payload) {
|
var tooltipContent = this._tooltipContent;
|
|
if (!this._alwaysShowContent) {
|
tooltipContent.hideLater(this._tooltipModel.get('hideDelay'));
|
}
|
|
this._lastX = this._lastY = null;
|
|
if (payload.from !== this.uid) {
|
this._hide(makeDispatchAction(payload, api));
|
}
|
},
|
|
// Be compatible with previous design, that is, when tooltip.type is 'axis' and
|
// dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer
|
// and tooltip.
|
_manuallyAxisShowTip: function (tooltipModel, ecModel, api, payload) {
|
var seriesIndex = payload.seriesIndex;
|
var dataIndex = payload.dataIndex;
|
var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo;
|
|
if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) {
|
return;
|
}
|
|
var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
|
if (!seriesModel) {
|
return;
|
}
|
|
var data = seriesModel.getData();
|
var tooltipModel = buildTooltipModel([
|
data.getItemModel(dataIndex),
|
seriesModel,
|
(seriesModel.coordinateSystem || {}).model,
|
tooltipModel
|
]);
|
|
if (tooltipModel.get('trigger') !== 'axis') {
|
return;
|
}
|
|
api.dispatchAction({
|
type: 'updateAxisPointer',
|
seriesIndex: seriesIndex,
|
dataIndex: dataIndex
|
});
|
|
return true;
|
},
|
|
_tryShow: function (e, dispatchAction) {
|
var el = e.target;
|
var tooltipModel = this._tooltipModel;
|
|
if (!tooltipModel) {
|
return;
|
}
|
|
// Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed
|
this._lastX = e.offsetX;
|
this._lastY = e.offsetY;
|
|
var dataByCoordSys = e.dataByCoordSys;
|
if (dataByCoordSys && dataByCoordSys.length) {
|
this._showAxisTooltip(dataByCoordSys, e);
|
}
|
// Always show item tooltip if mouse is on the element with dataIndex
|
else if (el && el.dataIndex != null) {
|
this._lastDataByCoordSys = null;
|
this._showSeriesItemTooltip(e, el, dispatchAction);
|
}
|
// Tooltip provided directly. Like legend.
|
else if (el && el.tooltip) {
|
this._lastDataByCoordSys = null;
|
this._showComponentItemTooltip(e, el, dispatchAction);
|
}
|
else {
|
this._lastDataByCoordSys = null;
|
this._hide(dispatchAction);
|
}
|
},
|
|
_showOrMove: function (tooltipModel, cb) {
|
// showDelay is used in this case: tooltip.enterable is set
|
// as true. User intent to move mouse into tooltip and click
|
// something. `showDelay` makes it easyer to enter the content
|
// but tooltip do not move immediately.
|
var delay = tooltipModel.get('showDelay');
|
cb = zrUtil.bind(cb, this);
|
clearTimeout(this._showTimout);
|
delay > 0
|
? (this._showTimout = setTimeout(cb, delay))
|
: cb();
|
},
|
|
_showAxisTooltip: function (dataByCoordSys, e) {
|
var ecModel = this._ecModel;
|
var globalTooltipModel = this._tooltipModel;
|
var point = [e.offsetX, e.offsetY];
|
var singleDefaultHTML = [];
|
var singleParamsList = [];
|
var singleTooltipModel = buildTooltipModel([
|
e.tooltipOption,
|
globalTooltipModel
|
]);
|
|
each(dataByCoordSys, function (itemCoordSys) {
|
// var coordParamList = [];
|
// var coordDefaultHTML = [];
|
// var coordTooltipModel = buildTooltipModel([
|
// e.tooltipOption,
|
// itemCoordSys.tooltipOption,
|
// ecModel.getComponent(itemCoordSys.coordSysMainType, itemCoordSys.coordSysIndex),
|
// globalTooltipModel
|
// ]);
|
// var displayMode = coordTooltipModel.get('displayMode');
|
// var paramsList = displayMode === 'single' ? singleParamsList : [];
|
|
each(itemCoordSys.dataByAxis, function (item) {
|
var axisModel = ecModel.getComponent(item.axisDim + 'Axis', item.axisIndex);
|
var axisValue = item.value;
|
var seriesDefaultHTML = [];
|
|
if (!axisModel || axisValue == null) {
|
return;
|
}
|
|
var valueLabel = axisPointerViewHelper.getValueLabel(
|
axisValue, axisModel.axis, ecModel,
|
item.seriesDataIndices,
|
item.valueLabelOpt
|
);
|
|
zrUtil.each(item.seriesDataIndices, function (idxItem) {
|
var series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
|
var dataIndex = idxItem.dataIndexInside;
|
var dataParams = series && series.getDataParams(dataIndex);
|
dataParams.axisDim = item.axisDim;
|
dataParams.axisIndex = item.axisIndex;
|
dataParams.axisType = item.axisType;
|
dataParams.axisId = item.axisId;
|
dataParams.axisValue = axisHelper.getAxisRawValue(axisModel.axis, axisValue);
|
dataParams.axisValueLabel = valueLabel;
|
|
if (dataParams) {
|
singleParamsList.push(dataParams);
|
seriesDefaultHTML.push(series.formatTooltip(dataIndex, true));
|
}
|
});
|
|
// Default tooltip content
|
// FIXME
|
// (1) shold be the first data which has name?
|
// (2) themeRiver, firstDataIndex is array, and first line is unnecessary.
|
var firstLine = valueLabel;
|
singleDefaultHTML.push(
|
(firstLine ? formatUtil.encodeHTML(firstLine) + '<br />' : '')
|
+ seriesDefaultHTML.join('<br />')
|
);
|
});
|
}, this);
|
|
// In most case, the second axis is shown upper than the first one.
|
singleDefaultHTML.reverse();
|
singleDefaultHTML = singleDefaultHTML.join('<br /><br />');
|
|
var positionExpr = e.position;
|
this._showOrMove(singleTooltipModel, function () {
|
if (this._updateContentNotChangedOnAxis(dataByCoordSys)) {
|
this._updatePosition(
|
singleTooltipModel,
|
positionExpr,
|
point[0], point[1],
|
this._tooltipContent,
|
singleParamsList
|
);
|
}
|
else {
|
this._showTooltipContent(
|
singleTooltipModel, singleDefaultHTML, singleParamsList, Math.random(),
|
point[0], point[1], positionExpr
|
);
|
}
|
});
|
|
// Do not trigger events here, because this branch only be entered
|
// from dispatchAction.
|
},
|
|
_showSeriesItemTooltip: function (e, el, dispatchAction) {
|
var ecModel = this._ecModel;
|
// Use dataModel in element if possible
|
// Used when mouseover on a element like markPoint or edge
|
// In which case, the data is not main data in series.
|
var seriesIndex = el.seriesIndex;
|
var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
|
|
// For example, graph link.
|
var dataModel = el.dataModel || seriesModel;
|
var dataIndex = el.dataIndex;
|
var dataType = el.dataType;
|
var data = dataModel.getData();
|
|
var tooltipModel = buildTooltipModel([
|
data.getItemModel(dataIndex),
|
dataModel,
|
seriesModel && (seriesModel.coordinateSystem || {}).model,
|
this._tooltipModel
|
]);
|
|
var tooltipTrigger = tooltipModel.get('trigger');
|
if (tooltipTrigger != null && tooltipTrigger !== 'item') {
|
return;
|
}
|
|
var params = dataModel.getDataParams(dataIndex, dataType);
|
var defaultHtml = dataModel.formatTooltip(dataIndex, false, dataType);
|
var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex;
|
|
this._showOrMove(tooltipModel, function () {
|
this._showTooltipContent(
|
tooltipModel, defaultHtml, params, asyncTicket,
|
e.offsetX, e.offsetY, e.position, e.target
|
);
|
});
|
|
// FIXME
|
// duplicated showtip if manuallyShowTip is called from dispatchAction.
|
dispatchAction({
|
type: 'showTip',
|
dataIndexInside: dataIndex,
|
dataIndex: data.getRawIndex(dataIndex),
|
seriesIndex: seriesIndex,
|
from: this.uid
|
});
|
},
|
|
_showComponentItemTooltip: function (e, el, dispatchAction) {
|
var tooltipOpt = el.tooltip;
|
if (typeof tooltipOpt === 'string') {
|
var content = tooltipOpt;
|
tooltipOpt = {
|
content: content,
|
// Fixed formatter
|
formatter: content
|
};
|
}
|
var subTooltipModel = new Model(tooltipOpt, this._tooltipModel, this._ecModel);
|
var defaultHtml = subTooltipModel.get('content');
|
var asyncTicket = Math.random();
|
|
// Do not check whether `trigger` is 'none' here, because `trigger`
|
// only works on cooridinate system. In fact, we have not found case
|
// that requires setting `trigger` nothing on component yet.
|
|
this._showOrMove(subTooltipModel, function () {
|
this._showTooltipContent(
|
subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {},
|
asyncTicket, e.offsetX, e.offsetY, e.position, el
|
);
|
});
|
|
// If not dispatch showTip, tip may be hide triggered by axis.
|
dispatchAction({
|
type: 'showTip',
|
from: this.uid
|
});
|
},
|
|
_showTooltipContent: function (
|
tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el
|
) {
|
// Reset ticket
|
this._ticket = '';
|
|
if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) {
|
return;
|
}
|
|
var tooltipContent = this._tooltipContent;
|
|
var formatter = tooltipModel.get('formatter');
|
positionExpr = positionExpr || tooltipModel.get('position');
|
var html = defaultHtml;
|
|
if (formatter && typeof formatter === 'string') {
|
html = formatUtil.formatTpl(formatter, params, true);
|
}
|
else if (typeof formatter === 'function') {
|
var callback = bind(function (cbTicket, html) {
|
if (cbTicket === this._ticket) {
|
tooltipContent.setContent(html);
|
this._updatePosition(
|
tooltipModel, positionExpr, x, y, tooltipContent, params, el
|
);
|
}
|
}, this);
|
this._ticket = asyncTicket;
|
html = formatter(params, asyncTicket, callback);
|
}
|
|
tooltipContent.setContent(html);
|
tooltipContent.show(tooltipModel);
|
|
this._updatePosition(
|
tooltipModel, positionExpr, x, y, tooltipContent, params, el
|
);
|
},
|
|
/**
|
* @param {string|Function|Array.<number>} positionExpr
|
* @param {number} x Mouse x
|
* @param {number} y Mouse y
|
* @param {boolean} confine Whether confine tooltip content in view rect.
|
* @param {Object|<Array.<Object>} params
|
* @param {module:zrender/Element} el target element
|
* @param {module:echarts/ExtensionAPI} api
|
* @return {Array.<number>}
|
*/
|
_updatePosition: function (tooltipModel, positionExpr, x, y, content, params, el) {
|
var viewWidth = this._api.getWidth();
|
var viewHeight = this._api.getHeight();
|
positionExpr = positionExpr || tooltipModel.get('position');
|
|
var contentSize = content.getSize();
|
var align = tooltipModel.get('align');
|
var vAlign = tooltipModel.get('verticalAlign');
|
var rect = el && el.getBoundingRect().clone();
|
el && rect.applyTransform(el.transform);
|
|
if (typeof positionExpr === 'function') {
|
// Callback of position can be an array or a string specify the position
|
positionExpr = positionExpr([x, y], params, content.el, rect, {
|
viewSize: [viewWidth, viewHeight],
|
contentSize: contentSize.slice()
|
});
|
}
|
|
if (zrUtil.isArray(positionExpr)) {
|
x = parsePercent(positionExpr[0], viewWidth);
|
y = parsePercent(positionExpr[1], viewHeight);
|
}
|
else if (zrUtil.isObject(positionExpr)) {
|
positionExpr.width = contentSize[0];
|
positionExpr.height = contentSize[1];
|
var layoutRect = layoutUtil.getLayoutRect(
|
positionExpr, {width: viewWidth, height: viewHeight}
|
);
|
x = layoutRect.x;
|
y = layoutRect.y;
|
align = null;
|
// When positionExpr is left/top/right/bottom,
|
// align and verticalAlign will not work.
|
vAlign = null;
|
}
|
// Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
|
else if (typeof positionExpr === 'string' && el) {
|
var pos = calcTooltipPosition(
|
positionExpr, rect, contentSize
|
);
|
x = pos[0];
|
y = pos[1];
|
}
|
else {
|
var pos = refixTooltipPosition(
|
x, y, content.el, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20
|
);
|
x = pos[0];
|
y = pos[1];
|
}
|
|
align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0);
|
vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0);
|
|
if (tooltipModel.get('confine')) {
|
var pos = confineTooltipPosition(
|
x, y, content.el, viewWidth, viewHeight
|
);
|
x = pos[0];
|
y = pos[1];
|
}
|
|
content.moveTo(x, y);
|
},
|
|
// FIXME
|
// Should we remove this but leave this to user?
|
_updateContentNotChangedOnAxis: function (dataByCoordSys) {
|
var lastCoordSys = this._lastDataByCoordSys;
|
var contentNotChanged = !!lastCoordSys
|
&& lastCoordSys.length === dataByCoordSys.length;
|
|
each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) {
|
var lastDataByAxis = lastItemCoordSys.dataByAxis || {};
|
var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {};
|
var thisDataByAxis = thisItemCoordSys.dataByAxis || [];
|
contentNotChanged &= lastDataByAxis.length === thisDataByAxis.length;
|
|
each(lastDataByAxis, function (lastItem, indexAxis) {
|
var thisItem = thisDataByAxis[indexAxis] || {};
|
var lastIndices = lastItem.seriesDataIndices || [];
|
var newIndices = thisItem.seriesDataIndices || [];
|
|
contentNotChanged &=
|
lastItem.value === thisItem.value
|
&& lastItem.axisType === thisItem.axisType
|
&& lastItem.axisId === thisItem.axisId
|
&& lastIndices.length === newIndices.length;
|
|
each(lastIndices, function (lastIdxItem, j) {
|
var newIdxItem = newIndices[j];
|
contentNotChanged &=
|
lastIdxItem.seriesIndex === newIdxItem.seriesIndex
|
&& lastIdxItem.dataIndex === newIdxItem.dataIndex;
|
});
|
});
|
});
|
|
this._lastDataByCoordSys = dataByCoordSys;
|
|
return !!contentNotChanged;
|
},
|
|
_hide: function (dispatchAction) {
|
// Do not directly hideLater here, because this behavior may be prevented
|
// in dispatchAction when showTip is dispatched.
|
|
// FIXME
|
// duplicated hideTip if manuallyHideTip is called from dispatchAction.
|
this._lastDataByCoordSys = null;
|
dispatchAction({
|
type: 'hideTip',
|
from: this.uid
|
});
|
},
|
|
dispose: function (ecModel, api) {
|
if (env.node) {
|
return;
|
}
|
this._tooltipContent.hide();
|
globalListener.unregister('itemTooltip', api);
|
}
|
});
|
|
|
/**
|
* @param {Array.<Object|module:echarts/model/Model>} modelCascade
|
* From top to bottom. (the last one should be globalTooltipModel);
|
*/
|
function buildTooltipModel(modelCascade) {
|
var resultModel = modelCascade.pop();
|
while (modelCascade.length) {
|
var tooltipOpt = modelCascade.pop();
|
if (tooltipOpt) {
|
if (tooltipOpt instanceof Model) {
|
tooltipOpt = tooltipOpt.get('tooltip', true);
|
}
|
// In each data item tooltip can be simply write:
|
// {
|
// value: 10,
|
// tooltip: 'Something you need to know'
|
// }
|
if (typeof tooltipOpt === 'string') {
|
tooltipOpt = {formatter: tooltipOpt};
|
}
|
resultModel = new Model(tooltipOpt, resultModel, resultModel.ecModel);
|
}
|
}
|
return resultModel;
|
}
|
|
function makeDispatchAction(payload, api) {
|
return payload.dispatchAction || zrUtil.bind(api.dispatchAction, api);
|
}
|
|
function refixTooltipPosition(x, y, el, viewWidth, viewHeight, gapH, gapV) {
|
var width = el.clientWidth;
|
var height = el.clientHeight;
|
|
if (gapH != null) {
|
if (x + width + gapH > viewWidth) {
|
x -= width + gapH;
|
}
|
else {
|
x += gapH;
|
}
|
}
|
if (gapV != null) {
|
if (y + height + gapV > viewHeight) {
|
y -= height + gapV;
|
}
|
else {
|
y += gapV;
|
}
|
}
|
return [x, y];
|
}
|
|
function confineTooltipPosition(x, y, el, viewWidth, viewHeight) {
|
var width = el.clientWidth;
|
var height = el.clientHeight;
|
|
x = Math.min(x + width, viewWidth) - width;
|
y = Math.min(y + height, viewHeight) - height;
|
x = Math.max(x, 0);
|
y = Math.max(y, 0);
|
|
return [x, y];
|
}
|
|
function calcTooltipPosition(position, rect, contentSize) {
|
var domWidth = contentSize[0];
|
var domHeight = contentSize[1];
|
var gap = 5;
|
var x = 0;
|
var y = 0;
|
var rectWidth = rect.width;
|
var rectHeight = rect.height;
|
switch (position) {
|
case 'inside':
|
x = rect.x + rectWidth / 2 - domWidth / 2;
|
y = rect.y + rectHeight / 2 - domHeight / 2;
|
break;
|
case 'top':
|
x = rect.x + rectWidth / 2 - domWidth / 2;
|
y = rect.y - domHeight - gap;
|
break;
|
case 'bottom':
|
x = rect.x + rectWidth / 2 - domWidth / 2;
|
y = rect.y + rectHeight + gap;
|
break;
|
case 'left':
|
x = rect.x - domWidth - gap;
|
y = rect.y + rectHeight / 2 - domHeight / 2;
|
break;
|
case 'right':
|
x = rect.x + rectWidth + gap;
|
y = rect.y + rectHeight / 2 - domHeight / 2;
|
}
|
return [x, y];
|
}
|
|
function isCenterAlign(align) {
|
return align === 'center' || align === 'middle';
|
}
|
|
|
|
/***/ },
|
/* 323 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/component/tooltip/TooltipContent
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var zrColor = __webpack_require__(39);
|
var eventUtil = __webpack_require__(88);
|
var formatUtil = __webpack_require__(6);
|
var each = zrUtil.each;
|
var toCamelCase = formatUtil.toCamelCase;
|
var env = __webpack_require__(2);
|
|
var vendors = ['', '-webkit-', '-moz-', '-o-'];
|
|
var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
|
|
/**
|
* @param {number} duration
|
* @return {string}
|
* @inner
|
*/
|
function assembleTransition(duration) {
|
var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
|
var transitionText = 'left ' + duration + 's ' + transitionCurve + ','
|
+ 'top ' + duration + 's ' + transitionCurve;
|
return zrUtil.map(vendors, function (vendorPrefix) {
|
return vendorPrefix + 'transition:' + transitionText;
|
}).join(';');
|
}
|
|
/**
|
* @param {Object} textStyle
|
* @return {string}
|
* @inner
|
*/
|
function assembleFont(textStyleModel) {
|
var cssText = [];
|
|
var fontSize = textStyleModel.get('fontSize');
|
var color = textStyleModel.getTextColor();
|
|
color && cssText.push('color:' + color);
|
|
cssText.push('font:' + textStyleModel.getFont());
|
|
fontSize &&
|
cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px');
|
|
each(['decoration', 'align'], function (name) {
|
var val = textStyleModel.get(name);
|
val && cssText.push('text-' + name + ':' + val);
|
});
|
|
return cssText.join(';');
|
}
|
|
/**
|
* @param {Object} tooltipModel
|
* @return {string}
|
* @inner
|
*/
|
function assembleCssText(tooltipModel) {
|
|
var cssText = [];
|
|
var transitionDuration = tooltipModel.get('transitionDuration');
|
var backgroundColor = tooltipModel.get('backgroundColor');
|
var textStyleModel = tooltipModel.getModel('textStyle');
|
var padding = tooltipModel.get('padding');
|
|
// Animation transition. Do not animate when transitionDuration is 0.
|
transitionDuration &&
|
cssText.push(assembleTransition(transitionDuration));
|
|
if (backgroundColor) {
|
if (env.canvasSupported) {
|
cssText.push('background-Color:' + backgroundColor);
|
}
|
else {
|
// for ie
|
cssText.push(
|
'background-Color:#' + zrColor.toHex(backgroundColor)
|
);
|
cssText.push('filter:alpha(opacity=70)');
|
}
|
}
|
|
// Border style
|
each(['width', 'color', 'radius'], function (name) {
|
var borderName = 'border-' + name;
|
var camelCase = toCamelCase(borderName);
|
var val = tooltipModel.get(camelCase);
|
val != null &&
|
cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px'));
|
});
|
|
// Text style
|
cssText.push(assembleFont(textStyleModel));
|
|
// Padding
|
if (padding != null) {
|
cssText.push('padding:' + formatUtil.normalizeCssArray(padding).join('px ') + 'px');
|
}
|
|
return cssText.join(';') + ';';
|
}
|
|
/**
|
* @alias module:echarts/component/tooltip/TooltipContent
|
* @constructor
|
*/
|
function TooltipContent(container, api) {
|
var el = document.createElement('div');
|
var zr = this._zr = api.getZr();
|
|
this.el = el;
|
|
this._x = api.getWidth() / 2;
|
this._y = api.getHeight() / 2;
|
|
container.appendChild(el);
|
|
this._container = container;
|
|
this._show = false;
|
|
/**
|
* @private
|
*/
|
this._hideTimeout;
|
|
var self = this;
|
el.onmouseenter = function () {
|
// clear the timeout in hideLater and keep showing tooltip
|
if (self._enterable) {
|
clearTimeout(self._hideTimeout);
|
self._show = true;
|
}
|
self._inContent = true;
|
};
|
el.onmousemove = function (e) {
|
e = e || window.event;
|
if (!self._enterable) {
|
// Try trigger zrender event to avoid mouse
|
// in and out shape too frequently
|
var handler = zr.handler;
|
eventUtil.normalizeEvent(container, e, true);
|
handler.dispatch('mousemove', e);
|
}
|
};
|
el.onmouseleave = function () {
|
if (self._enterable) {
|
if (self._show) {
|
self.hideLater(self._hideDelay);
|
}
|
}
|
self._inContent = false;
|
};
|
}
|
|
TooltipContent.prototype = {
|
|
constructor: TooltipContent,
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
_enterable: true,
|
|
/**
|
* Update when tooltip is rendered
|
*/
|
update: function () {
|
// FIXME
|
// Move this logic to ec main?
|
var container = this._container;
|
var stl = container.currentStyle
|
|| document.defaultView.getComputedStyle(container);
|
var domStyle = container.style;
|
if (domStyle.position !== 'absolute' && stl.position !== 'absolute') {
|
domStyle.position = 'relative';
|
}
|
// Hide the tooltip
|
// PENDING
|
// this.hide();
|
},
|
|
show: function (tooltipModel) {
|
clearTimeout(this._hideTimeout);
|
var el = this.el;
|
|
el.style.cssText = gCssText + assembleCssText(tooltipModel)
|
// http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore
|
+ ';left:' + this._x + 'px;top:' + this._y + 'px;'
|
+ (tooltipModel.get('extraCssText') || '');
|
|
el.style.display = el.innerHTML ? 'block' : 'none';
|
|
this._show = true;
|
},
|
|
setContent: function (content) {
|
this.el.innerHTML = content == null ? '' : content;
|
},
|
|
setEnterable: function (enterable) {
|
this._enterable = enterable;
|
},
|
|
getSize: function () {
|
var el = this.el;
|
return [el.clientWidth, el.clientHeight];
|
},
|
|
moveTo: function (x, y) {
|
// xy should be based on canvas root. But tooltipContent is
|
// the sibling of canvas root. So padding of ec container
|
// should be considered here.
|
var zr = this._zr;
|
var viewportRoot;
|
if (zr && zr.painter && (viewportRoot = zr.painter.getViewportRoot())) {
|
x += viewportRoot.offsetLeft || 0;
|
y += viewportRoot.offsetTop || 0;
|
}
|
|
var style = this.el.style;
|
style.left = x + 'px';
|
style.top = y + 'px';
|
|
this._x = x;
|
this._y = y;
|
},
|
|
hide: function () {
|
this.el.style.display = 'none';
|
this._show = false;
|
},
|
|
hideLater: function (time) {
|
if (this._show && !(this._inContent && this._enterable)) {
|
if (time) {
|
this._hideDelay = time;
|
// Set show false to avoid invoke hideLater mutiple times
|
this._show = false;
|
this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time);
|
}
|
else {
|
this.hide();
|
}
|
}
|
},
|
|
isShow: function () {
|
return this._show;
|
}
|
};
|
|
module.exports = TooltipContent;
|
|
|
/***/ },
|
/* 324 */,
|
/* 325 */,
|
/* 326 */,
|
/* 327 */,
|
/* 328 */,
|
/* 329 */,
|
/* 330 */,
|
/* 331 */,
|
/* 332 */,
|
/* 333 */,
|
/* 334 */,
|
/* 335 */,
|
/* 336 */,
|
/* 337 */,
|
/* 338 */,
|
/* 339 */,
|
/* 340 */,
|
/* 341 */,
|
/* 342 */,
|
/* 343 */,
|
/* 344 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var modelUtil = __webpack_require__(5);
|
var brushHelper = __webpack_require__(245);
|
|
var each = zrUtil.each;
|
var indexOf = zrUtil.indexOf;
|
var curry = zrUtil.curry;
|
|
var COORD_CONVERTS = ['dataToPoint', 'pointToData'];
|
|
// FIXME
|
// how to genarialize to more coordinate systems.
|
var INCLUDE_FINDER_MAIN_TYPES = [
|
'grid', 'xAxis', 'yAxis', 'geo', 'graph',
|
'polar', 'radiusAxis', 'angleAxis', 'bmap'
|
];
|
|
/**
|
* [option in constructor]:
|
* {
|
* Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
|
* }
|
*
|
*
|
* [targetInfo]:
|
*
|
* There can be multiple axes in a single targetInfo. Consider the case
|
* of `grid` component, a targetInfo represents a grid which contains one or more
|
* cartesian and one or more axes. And consider the case of parallel system,
|
* which has multiple axes in a coordinate system.
|
* Can be {
|
* panelId: ...,
|
* coordSys: <a representitive cartesian in grid (first cartesian by default)>,
|
* coordSyses: all cartesians.
|
* gridModel: <grid component>
|
* xAxes: correspond to coordSyses on index
|
* yAxes: correspond to coordSyses on index
|
* }
|
* or {
|
* panelId: ...,
|
* coordSys: <geo coord sys>
|
* coordSyses: [<geo coord sys>]
|
* geoModel: <geo component>
|
* }
|
*
|
*
|
* [panelOpt]:
|
*
|
* Make from targetInfo. Input to BrushController.
|
* {
|
* panelId: ...,
|
* rect: ...
|
* }
|
*
|
*
|
* [area]:
|
*
|
* Generated by BrushController or user input.
|
* {
|
* panelId: Used to locate coordInfo directly. If user inpput, no panelId.
|
* brushType: determine how to convert to/from coord('rect' or 'polygon' or 'lineX/Y').
|
* Index/Id/Name of geo, xAxis, yAxis, grid: See util/model#parseFinder.
|
* range: pixel range.
|
* coordRange: representitive coord range (the first one of coordRanges).
|
* coordRanges: <Array> coord ranges, used in multiple cartesian in one grid.
|
* }
|
*/
|
|
/**
|
* @param {Object} option contains Index/Id/Name of xAxis/yAxis/geo/grid
|
* Each can be {number|Array.<number>}. like: {xAxisIndex: [3, 4]}
|
* @param {module:echarts/model/Global} ecModel
|
* @param {Object} [opt]
|
* @param {Array.<string>} [opt.include] include coordinate system types.
|
*/
|
function BrushTargetManager(option, ecModel, opt) {
|
/**
|
* @private
|
* @type {Array.<Object>}
|
*/
|
var targetInfoList = this._targetInfoList = [];
|
var info = {};
|
var foundCpts = parseFinder(ecModel, option);
|
|
each(targetInfoBuilders, function (builder, type) {
|
if (!opt || !opt.include || indexOf(opt.include, type) >= 0) {
|
builder(foundCpts, targetInfoList, info);
|
}
|
});
|
}
|
|
var proto = BrushTargetManager.prototype;
|
|
proto.setOutputRanges = function (areas, ecModel) {
|
this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
|
(area.coordRanges || (area.coordRanges = [])).push(coordRange);
|
// area.coordRange is the first of area.coordRanges
|
if (!area.coordRange) {
|
area.coordRange = coordRange;
|
// In 'category' axis, coord to pixel is not reversible, so we can not
|
// rebuild range by coordRange accrately, which may bring trouble when
|
// brushing only one item. So we use __rangeOffset to rebuilding range
|
// by coordRange. And this it only used in brush component so it is no
|
// need to be adapted to coordRanges.
|
var result = coordConvert[area.brushType](0, coordSys, coordRange);
|
area.__rangeOffset = {
|
offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]),
|
xyMinMax: result.xyMinMax
|
};
|
}
|
});
|
};
|
|
proto.matchOutputRanges = function (areas, ecModel, cb) {
|
each(areas, function (area) {
|
var targetInfo = this.findTargetInfo(area, ecModel);
|
|
if (targetInfo && targetInfo !== true) {
|
zrUtil.each(
|
targetInfo.coordSyses,
|
function (coordSys) {
|
var result = coordConvert[area.brushType](1, coordSys, area.range);
|
cb(area, result.values, coordSys, ecModel);
|
}
|
);
|
}
|
}, this);
|
};
|
|
proto.setInputRanges = function (areas, ecModel) {
|
each(areas, function (area) {
|
var targetInfo = this.findTargetInfo(area, ecModel);
|
|
if (true) {
|
zrUtil.assert(
|
!targetInfo || targetInfo === true || area.coordRange,
|
'coordRange must be specified when coord index specified.'
|
);
|
zrUtil.assert(
|
!targetInfo || targetInfo !== true || area.range,
|
'range must be specified in global brush.'
|
);
|
}
|
|
area.range = area.range || [];
|
|
// convert coordRange to global range and set panelId.
|
if (targetInfo && targetInfo !== true) {
|
area.panelId = targetInfo.panelId;
|
// (1) area.range shoule always be calculate from coordRange but does
|
// not keep its original value, for the sake of the dataZoom scenario,
|
// where area.coordRange remains unchanged but area.range may be changed.
|
// (2) Only support converting one coordRange to pixel range in brush
|
// component. So do not consider `coordRanges`.
|
// (3) About __rangeOffset, see comment above.
|
var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange);
|
var rangeOffset = area.__rangeOffset;
|
area.range = rangeOffset
|
? diffProcessor[area.brushType](
|
result.values,
|
rangeOffset.offset,
|
getScales(result.xyMinMax, rangeOffset.xyMinMax)
|
)
|
: result.values;
|
}
|
}, this);
|
};
|
|
proto.makePanelOpts = function (api, getDefaultBrushType) {
|
return zrUtil.map(this._targetInfoList, function (targetInfo) {
|
var rect = targetInfo.getPanelRect();
|
return {
|
panelId: targetInfo.panelId,
|
defaultBrushType: getDefaultBrushType && getDefaultBrushType(targetInfo),
|
clipPath: brushHelper.makeRectPanelClipPath(rect),
|
isTargetByCursor: brushHelper.makeRectIsTargetByCursor(
|
rect, api, targetInfo.coordSysModel
|
),
|
getLinearBrushOtherExtent: brushHelper.makeLinearBrushOtherExtent(rect)
|
};
|
});
|
};
|
|
proto.controlSeries = function (area, seriesModel, ecModel) {
|
// Check whether area is bound in coord, and series do not belong to that coord.
|
// If do not do this check, some brush (like lineX) will controll all axes.
|
var targetInfo = this.findTargetInfo(area, ecModel);
|
return targetInfo === true || (
|
targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0
|
);
|
};
|
|
/**
|
* If return Object, a coord found.
|
* If reutrn true, global found.
|
* Otherwise nothing found.
|
*
|
* @param {Object} area
|
* @param {Array} targetInfoList
|
* @return {Obejct|boolean}
|
*/
|
proto.findTargetInfo = function (area, ecModel) {
|
var targetInfoList = this._targetInfoList;
|
var foundCpts = parseFinder(ecModel, area);
|
|
for (var i = 0; i < targetInfoList.length; i++) {
|
var targetInfo = targetInfoList[i];
|
var areaPanelId = area.panelId;
|
if (areaPanelId) {
|
if (targetInfo.panelId === areaPanelId) {
|
return targetInfo;
|
}
|
}
|
else {
|
for (var i = 0; i < targetInfoMatchers.length; i++) {
|
if (targetInfoMatchers[i](foundCpts, targetInfo)) {
|
return targetInfo;
|
}
|
}
|
}
|
}
|
|
return true;
|
};
|
|
function formatMinMax(minMax) {
|
minMax[0] > minMax[1] && minMax.reverse();
|
return minMax;
|
}
|
|
function parseFinder(ecModel, option) {
|
return modelUtil.parseFinder(
|
ecModel, option, {includeMainTypes: INCLUDE_FINDER_MAIN_TYPES}
|
);
|
}
|
|
var targetInfoBuilders = {
|
|
grid: function (foundCpts, targetInfoList) {
|
var xAxisModels = foundCpts.xAxisModels;
|
var yAxisModels = foundCpts.yAxisModels;
|
var gridModels = foundCpts.gridModels;
|
// Remove duplicated.
|
var gridModelMap = {};
|
var xAxesHas = {};
|
var yAxesHas = {};
|
|
if (!xAxisModels && !yAxisModels && !gridModels) {
|
return;
|
}
|
|
each(xAxisModels, function (axisModel) {
|
var gridModel = axisModel.axis.grid.model;
|
gridModelMap[gridModel.id] = gridModel;
|
xAxesHas[gridModel.id] = true;
|
});
|
each(yAxisModels, function (axisModel) {
|
var gridModel = axisModel.axis.grid.model;
|
gridModelMap[gridModel.id] = gridModel;
|
yAxesHas[gridModel.id] = true;
|
});
|
each(gridModels, function (gridModel) {
|
gridModelMap[gridModel.id] = gridModel;
|
xAxesHas[gridModel.id] = true;
|
yAxesHas[gridModel.id] = true;
|
});
|
|
each(gridModelMap, function (gridModel) {
|
var grid = gridModel.coordinateSystem;
|
var cartesians = [];
|
|
each(grid.getCartesians(), function (cartesian, index) {
|
if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0
|
|| indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0
|
) {
|
cartesians.push(cartesian);
|
}
|
});
|
targetInfoList.push({
|
panelId: 'grid--' + gridModel.id,
|
gridModel: gridModel,
|
coordSysModel: gridModel,
|
// Use the first one as the representitive coordSys.
|
coordSys: cartesians[0],
|
coordSyses: cartesians,
|
getPanelRect: panelRectBuilder.grid,
|
xAxisDeclared: xAxesHas[gridModel.id],
|
yAxisDeclared: yAxesHas[gridModel.id]
|
});
|
});
|
},
|
|
geo: function (foundCpts, targetInfoList) {
|
each(foundCpts.geoModels, function (geoModel) {
|
var coordSys = geoModel.coordinateSystem;
|
targetInfoList.push({
|
panelId: 'geo--' + geoModel.id,
|
geoModel: geoModel,
|
coordSysModel: geoModel,
|
coordSys: coordSys,
|
coordSyses: [coordSys],
|
getPanelRect: panelRectBuilder.geo
|
});
|
});
|
}
|
};
|
|
var targetInfoMatchers = [
|
|
// grid
|
function (foundCpts, targetInfo) {
|
var xAxisModel = foundCpts.xAxisModel;
|
var yAxisModel = foundCpts.yAxisModel;
|
var gridModel = foundCpts.gridModel;
|
|
!gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model);
|
!gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model);
|
|
return gridModel && gridModel === targetInfo.gridModel;
|
},
|
|
// geo
|
function (foundCpts, targetInfo) {
|
var geoModel = foundCpts.geoModel;
|
return geoModel && geoModel === targetInfo.geoModel;
|
}
|
];
|
|
var panelRectBuilder = {
|
|
grid: function () {
|
// grid is not Transformable.
|
return this.coordSys.grid.getRect().clone();
|
},
|
|
geo: function () {
|
var coordSys = this.coordSys;
|
var rect = coordSys.getBoundingRect().clone();
|
// geo roam and zoom transform
|
rect.applyTransform(graphic.getTransform(coordSys));
|
return rect;
|
}
|
};
|
|
var coordConvert = {
|
|
lineX: curry(axisConvert, 0),
|
|
lineY: curry(axisConvert, 1),
|
|
rect: function (to, coordSys, rangeOrCoordRange) {
|
var xminymin = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]]);
|
var xmaxymax = coordSys[COORD_CONVERTS[to]]([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]]);
|
var values = [
|
formatMinMax([xminymin[0], xmaxymax[0]]),
|
formatMinMax([xminymin[1], xmaxymax[1]])
|
];
|
return {values: values, xyMinMax: values};
|
},
|
|
polygon: function (to, coordSys, rangeOrCoordRange) {
|
var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]];
|
var values = zrUtil.map(rangeOrCoordRange, function (item) {
|
var p = coordSys[COORD_CONVERTS[to]](item);
|
xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]);
|
xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]);
|
xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]);
|
xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]);
|
return p;
|
});
|
return {values: values, xyMinMax: xyMinMax};
|
}
|
};
|
|
function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) {
|
if (true) {
|
zrUtil.assert(
|
coordSys.type === 'cartesian2d',
|
'lineX/lineY brush is available only in cartesian2d.'
|
);
|
}
|
|
var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]);
|
var values = formatMinMax(zrUtil.map([0, 1], function (i) {
|
return to
|
? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]))
|
: axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i]));
|
}));
|
var xyMinMax = [];
|
xyMinMax[axisNameIndex] = values;
|
xyMinMax[1 - axisNameIndex] = [NaN, NaN];
|
|
return {values: values, xyMinMax: xyMinMax};
|
}
|
|
var diffProcessor = {
|
lineX: curry(axisDiffProcessor, 0),
|
|
lineY: curry(axisDiffProcessor, 1),
|
|
rect: function (values, refer, scales) {
|
return [
|
[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]],
|
[values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]
|
];
|
},
|
|
polygon: function (values, refer, scales) {
|
return zrUtil.map(values, function (item, idx) {
|
return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]];
|
});
|
}
|
};
|
|
function axisDiffProcessor(axisNameIndex, values, refer, scales) {
|
return [
|
values[0] - scales[axisNameIndex] * refer[0],
|
values[1] - scales[axisNameIndex] * refer[1]
|
];
|
}
|
|
// We have to process scale caused by dataZoom manually,
|
// although it might be not accurate.
|
function getScales(xyMinMaxCurr, xyMinMaxOrigin) {
|
var sizeCurr = getSize(xyMinMaxCurr);
|
var sizeOrigin = getSize(xyMinMaxOrigin);
|
var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]];
|
isNaN(scales[0]) && (scales[0] = 1);
|
isNaN(scales[1]) && (scales[1] = 1);
|
return scales;
|
}
|
|
function getSize(xyMinMax) {
|
return xyMinMax
|
? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]]
|
: [NaN, NaN];
|
}
|
|
module.exports = BrushTargetManager;
|
|
|
/***/ },
|
/* 345 */,
|
/* 346 */,
|
/* 347 */,
|
/* 348 */,
|
/* 349 */
|
/***/ function(module, exports) {
|
|
'use strict';
|
|
|
var features = {};
|
|
module.exports = {
|
register: function (name, ctor) {
|
features[name] = ctor;
|
},
|
|
get: function (name) {
|
return features[name];
|
}
|
};
|
|
|
/***/ },
|
/* 350 */,
|
/* 351 */,
|
/* 352 */,
|
/* 353 */,
|
/* 354 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var echarts = __webpack_require__(1);
|
var graphic = __webpack_require__(44);
|
var layout = __webpack_require__(21);
|
|
// Model
|
echarts.extendComponentModel({
|
|
type: 'title',
|
|
layoutMode: {type: 'box', ignoreSize: true},
|
|
defaultOption: {
|
// 一级层叠
|
zlevel: 0,
|
// 二级层叠
|
z: 6,
|
show: true,
|
|
text: '',
|
// 超链接跳转
|
// link: null,
|
// 仅支持self | blank
|
target: 'blank',
|
subtext: '',
|
|
// 超链接跳转
|
// sublink: null,
|
// 仅支持self | blank
|
subtarget: 'blank',
|
|
// 'center' ¦ 'left' ¦ 'right'
|
// ¦ {number}(x坐标,单位px)
|
left: 0,
|
// 'top' ¦ 'bottom' ¦ 'center'
|
// ¦ {number}(y坐标,单位px)
|
top: 0,
|
|
// 水平对齐
|
// 'auto' | 'left' | 'right' | 'center'
|
// 默认根据 left 的位置判断是左对齐还是右对齐
|
// textAlign: null
|
//
|
// 垂直对齐
|
// 'auto' | 'top' | 'bottom' | 'middle'
|
// 默认根据 top 位置判断是上对齐还是下对齐
|
// textBaseline: null
|
|
backgroundColor: 'rgba(0,0,0,0)',
|
|
// 标题边框颜色
|
borderColor: '#ccc',
|
|
// 标题边框线宽,单位px,默认为0(无边框)
|
borderWidth: 0,
|
|
// 标题内边距,单位px,默认各方向内边距为5,
|
// 接受数组分别设定上右下左边距,同css
|
padding: 5,
|
|
// 主副标题纵向间隔,单位px,默认为10,
|
itemGap: 10,
|
textStyle: {
|
fontSize: 18,
|
fontWeight: 'bolder',
|
color: '#333'
|
},
|
subtextStyle: {
|
color: '#aaa'
|
}
|
}
|
});
|
|
// View
|
echarts.extendComponentView({
|
|
type: 'title',
|
|
render: function (titleModel, ecModel, api) {
|
this.group.removeAll();
|
|
if (!titleModel.get('show')) {
|
return;
|
}
|
|
var group = this.group;
|
|
var textStyleModel = titleModel.getModel('textStyle');
|
var subtextStyleModel = titleModel.getModel('subtextStyle');
|
|
var textAlign = titleModel.get('textAlign');
|
var textBaseline = titleModel.get('textBaseline');
|
|
var textEl = new graphic.Text({
|
style: {
|
text: titleModel.get('text'),
|
textFont: textStyleModel.getFont(),
|
fill: textStyleModel.getTextColor()
|
},
|
z2: 10
|
});
|
|
var textRect = textEl.getBoundingRect();
|
|
var subText = titleModel.get('subtext');
|
var subTextEl = new graphic.Text({
|
style: {
|
text: subText,
|
textFont: subtextStyleModel.getFont(),
|
fill: subtextStyleModel.getTextColor(),
|
y: textRect.height + titleModel.get('itemGap'),
|
textBaseline: 'top'
|
},
|
z2: 10
|
});
|
|
var link = titleModel.get('link');
|
var sublink = titleModel.get('sublink');
|
|
textEl.silent = !link;
|
subTextEl.silent = !sublink;
|
|
if (link) {
|
textEl.on('click', function () {
|
window.open(link, '_' + titleModel.get('target'));
|
});
|
}
|
if (sublink) {
|
subTextEl.on('click', function () {
|
window.open(sublink, '_' + titleModel.get('subtarget'));
|
});
|
}
|
|
group.add(textEl);
|
subText && group.add(subTextEl);
|
// If no subText, but add subTextEl, there will be an empty line.
|
|
var groupRect = group.getBoundingRect();
|
var layoutOption = titleModel.getBoxLayoutParams();
|
layoutOption.width = groupRect.width;
|
layoutOption.height = groupRect.height;
|
var layoutRect = layout.getLayoutRect(
|
layoutOption, {
|
width: api.getWidth(),
|
height: api.getHeight()
|
}, titleModel.get('padding')
|
);
|
// Adjust text align based on position
|
if (!textAlign) {
|
// Align left if title is on the left. center and right is same
|
textAlign = titleModel.get('left') || titleModel.get('right');
|
if (textAlign === 'middle') {
|
textAlign = 'center';
|
}
|
// Adjust layout by text align
|
if (textAlign === 'right') {
|
layoutRect.x += layoutRect.width;
|
}
|
else if (textAlign === 'center') {
|
layoutRect.x += layoutRect.width / 2;
|
}
|
}
|
if (!textBaseline) {
|
textBaseline = titleModel.get('top') || titleModel.get('bottom');
|
if (textBaseline === 'center') {
|
textBaseline = 'middle';
|
}
|
if (textBaseline === 'bottom') {
|
layoutRect.y += layoutRect.height;
|
}
|
else if (textBaseline === 'middle') {
|
layoutRect.y += layoutRect.height / 2;
|
}
|
|
textBaseline = textBaseline || 'top';
|
}
|
|
group.attr('position', [layoutRect.x, layoutRect.y]);
|
var alignStyle = {
|
textAlign: textAlign,
|
textVerticalAlign: textBaseline
|
};
|
textEl.setStyle(alignStyle);
|
subTextEl.setStyle(alignStyle);
|
|
// Render background
|
// Get groupRect again because textAlign has been changed
|
groupRect = group.getBoundingRect();
|
var padding = layoutRect.margin;
|
var style = titleModel.getItemStyle(['color', 'opacity']);
|
style.fill = titleModel.get('backgroundColor');
|
var rect = new graphic.Rect({
|
shape: {
|
x: groupRect.x - padding[3],
|
y: groupRect.y - padding[0],
|
width: groupRect.width + padding[1] + padding[3],
|
height: groupRect.height + padding[0] + padding[2]
|
},
|
style: style,
|
silent: true
|
});
|
graphic.subPixelOptimizeRect(rect);
|
|
group.add(rect);
|
}
|
});
|
|
|
/***/ },
|
/* 355 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* DataZoom component entry
|
*/
|
|
|
__webpack_require__(356);
|
|
__webpack_require__(357);
|
__webpack_require__(360);
|
|
__webpack_require__(361);
|
__webpack_require__(362);
|
|
__webpack_require__(363);
|
__webpack_require__(364);
|
|
__webpack_require__(366);
|
__webpack_require__(367);
|
|
|
|
/***/ },
|
/* 356 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(19).registerSubTypeDefaulter('dataZoom', function (option) {
|
// Default 'slider' when no type specified.
|
return 'slider';
|
});
|
|
|
|
/***/ },
|
/* 357 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom model
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var env = __webpack_require__(2);
|
var echarts = __webpack_require__(1);
|
var modelUtil = __webpack_require__(5);
|
var helper = __webpack_require__(358);
|
var AxisProxy = __webpack_require__(359);
|
var each = zrUtil.each;
|
var eachAxisDim = helper.eachAxisDim;
|
|
var DataZoomModel = echarts.extendComponentModel({
|
|
type: 'dataZoom',
|
|
dependencies: [
|
'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'
|
],
|
|
/**
|
* @protected
|
*/
|
defaultOption: {
|
zlevel: 0,
|
z: 4, // Higher than normal component (z: 2).
|
orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'.
|
xAxisIndex: null, // Default the first horizontal category axis.
|
yAxisIndex: null, // Default the first vertical category axis.
|
|
filterMode: 'filter', // Possible values: 'filter' or 'empty'.
|
// 'filter': data items which are out of window will be removed.
|
// This option is applicable when filtering outliers.
|
// 'empty': data items which are out of window will be set to empty.
|
// This option is applicable when user should not neglect
|
// that there are some data items out of window.
|
// Taking line chart as an example, line will be broken in
|
// the filtered points when filterModel is set to 'empty', but
|
// be connected when set to 'filter'.
|
|
throttle: null, // Dispatch action by the fixed rate, avoid frequency.
|
// default 100. Do not throttle when use null/undefined.
|
// If animation === true and animationDurationUpdate > 0,
|
// default value is 100, otherwise 20.
|
start: 0, // Start percent. 0 ~ 100
|
end: 100, // End percent. 0 ~ 100
|
startValue: null, // Start value. If startValue specified, start is ignored.
|
endValue: null // End value. If endValue specified, end is ignored.
|
},
|
|
/**
|
* @override
|
*/
|
init: function (option, parentModel, ecModel) {
|
|
/**
|
* key like x_0, y_1
|
* @private
|
* @type {Object}
|
*/
|
this._dataIntervalByAxis = {};
|
|
/**
|
* @private
|
*/
|
this._dataInfo = {};
|
|
/**
|
* key like x_0, y_1
|
* @private
|
*/
|
this._axisProxies = {};
|
|
/**
|
* @readOnly
|
*/
|
this.textStyleModel;
|
|
/**
|
* @private
|
*/
|
this._autoThrottle = true;
|
|
/**
|
* 'percent' or 'value'
|
* @private
|
*/
|
this._rangePropMode = ['percent', 'percent'];
|
|
var rawOption = retrieveRaw(option);
|
|
this.mergeDefaultAndTheme(option, ecModel);
|
|
this.doInit(rawOption);
|
},
|
|
/**
|
* @override
|
*/
|
mergeOption: function (newOption) {
|
var rawOption = retrieveRaw(newOption);
|
|
//FIX #2591
|
zrUtil.merge(this.option, newOption, true);
|
|
this.doInit(rawOption);
|
},
|
|
/**
|
* @protected
|
*/
|
doInit: function (rawOption) {
|
var thisOption = this.option;
|
|
// Disable realtime view update if canvas is not supported.
|
if (!env.canvasSupported) {
|
thisOption.realtime = false;
|
}
|
|
this._setDefaultThrottle(rawOption);
|
|
updateRangeUse(this, rawOption);
|
|
each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
|
// start/end has higher priority over startValue/endValue if they
|
// both set, but we should make chart.setOption({endValue: 1000})
|
// effective, rather than chart.setOption({endValue: 1000, end: null}).
|
if (this._rangePropMode[index] === 'value') {
|
thisOption[names[0]] = null;
|
}
|
// Otherwise do nothing and use the merge result.
|
}, this);
|
|
this.textStyleModel = this.getModel('textStyle');
|
|
this._resetTarget();
|
|
this._giveAxisProxies();
|
},
|
|
/**
|
* @private
|
*/
|
_giveAxisProxies: function () {
|
var axisProxies = this._axisProxies;
|
|
this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
|
var axisModel = this.dependentModels[dimNames.axis][axisIndex];
|
|
// If exists, share axisProxy with other dataZoomModels.
|
var axisProxy = axisModel.__dzAxisProxy || (
|
// Use the first dataZoomModel as the main model of axisProxy.
|
axisModel.__dzAxisProxy = new AxisProxy(
|
dimNames.name, axisIndex, this, ecModel
|
)
|
);
|
// FIXME
|
// dispose __dzAxisProxy
|
|
axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
|
}, this);
|
},
|
|
/**
|
* @private
|
*/
|
_resetTarget: function () {
|
var thisOption = this.option;
|
|
var autoMode = this._judgeAutoMode();
|
|
eachAxisDim(function (dimNames) {
|
var axisIndexName = dimNames.axisIndex;
|
thisOption[axisIndexName] = modelUtil.normalizeToArray(
|
thisOption[axisIndexName]
|
);
|
}, this);
|
|
if (autoMode === 'axisIndex') {
|
this._autoSetAxisIndex();
|
}
|
else if (autoMode === 'orient') {
|
this._autoSetOrient();
|
}
|
},
|
|
/**
|
* @private
|
*/
|
_judgeAutoMode: function () {
|
// Auto set only works for setOption at the first time.
|
// The following is user's reponsibility. So using merged
|
// option is OK.
|
var thisOption = this.option;
|
|
var hasIndexSpecified = false;
|
eachAxisDim(function (dimNames) {
|
// When user set axisIndex as a empty array, we think that user specify axisIndex
|
// but do not want use auto mode. Because empty array may be encountered when
|
// some error occured.
|
if (thisOption[dimNames.axisIndex] != null) {
|
hasIndexSpecified = true;
|
}
|
}, this);
|
|
var orient = thisOption.orient;
|
|
if (orient == null && hasIndexSpecified) {
|
return 'orient';
|
}
|
else if (!hasIndexSpecified) {
|
if (orient == null) {
|
thisOption.orient = 'horizontal';
|
}
|
return 'axisIndex';
|
}
|
},
|
|
/**
|
* @private
|
*/
|
_autoSetAxisIndex: function () {
|
var autoAxisIndex = true;
|
var orient = this.get('orient', true);
|
var thisOption = this.option;
|
var dependentModels = this.dependentModels;
|
|
if (autoAxisIndex) {
|
// Find axis that parallel to dataZoom as default.
|
var dimName = orient === 'vertical' ? 'y' : 'x';
|
|
if (dependentModels[dimName + 'Axis'].length) {
|
thisOption[dimName + 'AxisIndex'] = [0];
|
autoAxisIndex = false;
|
}
|
else {
|
each(dependentModels.singleAxis, function (singleAxisModel) {
|
if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
|
thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
|
autoAxisIndex = false;
|
}
|
});
|
}
|
}
|
|
if (autoAxisIndex) {
|
// Find the first category axis as default. (consider polar)
|
eachAxisDim(function (dimNames) {
|
if (!autoAxisIndex) {
|
return;
|
}
|
var axisIndices = [];
|
var axisModels = this.dependentModels[dimNames.axis];
|
if (axisModels.length && !axisIndices.length) {
|
for (var i = 0, len = axisModels.length; i < len; i++) {
|
if (axisModels[i].get('type') === 'category') {
|
axisIndices.push(i);
|
}
|
}
|
}
|
thisOption[dimNames.axisIndex] = axisIndices;
|
if (axisIndices.length) {
|
autoAxisIndex = false;
|
}
|
}, this);
|
}
|
|
if (autoAxisIndex) {
|
// FIXME
|
// 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
|
// 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?
|
|
// If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
|
// dataZoom component auto adopts series that reference to
|
// both xAxis and yAxis which type is 'value'.
|
this.ecModel.eachSeries(function (seriesModel) {
|
if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
|
eachAxisDim(function (dimNames) {
|
var axisIndices = thisOption[dimNames.axisIndex];
|
|
var axisIndex = seriesModel.get(dimNames.axisIndex);
|
var axisId = seriesModel.get(dimNames.axisId);
|
|
var axisModel = seriesModel.ecModel.queryComponents({
|
mainType: dimNames.axis,
|
index: axisIndex,
|
id: axisId
|
})[0];
|
|
if (true) {
|
if (!axisModel) {
|
throw new Error(
|
dimNames.axis + ' "' + zrUtil.retrieve(
|
axisIndex,
|
axisId,
|
0
|
) + '" not found'
|
);
|
}
|
}
|
axisIndex = axisModel.componentIndex;
|
|
if (zrUtil.indexOf(axisIndices, axisIndex) < 0) {
|
axisIndices.push(axisIndex);
|
}
|
});
|
}
|
}, this);
|
}
|
},
|
|
/**
|
* @private
|
*/
|
_autoSetOrient: function () {
|
var dim;
|
|
// Find the first axis
|
this.eachTargetAxis(function (dimNames) {
|
!dim && (dim = dimNames.name);
|
}, this);
|
|
this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
|
},
|
|
/**
|
* @private
|
*/
|
_isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) {
|
// FIXME
|
// 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
|
// 例如series.type === scatter时。
|
|
var is = true;
|
eachAxisDim(function (dimNames) {
|
var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
|
var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];
|
|
if (!axisModel || axisModel.get('type') !== axisType) {
|
is = false;
|
}
|
}, this);
|
return is;
|
},
|
|
/**
|
* @private
|
*/
|
_setDefaultThrottle: function (rawOption) {
|
// When first time user set throttle, auto throttle ends.
|
if (rawOption.hasOwnProperty('throttle')) {
|
this._autoThrottle = false;
|
}
|
if (this._autoThrottle) {
|
var globalOption = this.ecModel.option;
|
this.option.throttle =
|
(globalOption.animation && globalOption.animationDurationUpdate > 0)
|
? 100 : 20;
|
}
|
},
|
|
/**
|
* @public
|
*/
|
getFirstTargetAxisModel: function () {
|
var firstAxisModel;
|
eachAxisDim(function (dimNames) {
|
if (firstAxisModel == null) {
|
var indices = this.get(dimNames.axisIndex);
|
if (indices.length) {
|
firstAxisModel = this.dependentModels[dimNames.axis][indices[0]];
|
}
|
}
|
}, this);
|
|
return firstAxisModel;
|
},
|
|
/**
|
* @public
|
* @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
|
*/
|
eachTargetAxis: function (callback, context) {
|
var ecModel = this.ecModel;
|
eachAxisDim(function (dimNames) {
|
each(
|
this.get(dimNames.axisIndex),
|
function (axisIndex) {
|
callback.call(context, dimNames, axisIndex, this, ecModel);
|
},
|
this
|
);
|
}, this);
|
},
|
|
/**
|
* @param {string} dimName
|
* @param {number} axisIndex
|
* @return {module:echarts/component/dataZoom/AxisProxy} If not found, return null/undefined.
|
*/
|
getAxisProxy: function (dimName, axisIndex) {
|
return this._axisProxies[dimName + '_' + axisIndex];
|
},
|
|
/**
|
* @param {string} dimName
|
* @param {number} axisIndex
|
* @return {module:echarts/model/Model} If not found, return null/undefined.
|
*/
|
getAxisModel: function (dimName, axisIndex) {
|
var axisProxy = this.getAxisProxy(dimName, axisIndex);
|
return axisProxy && axisProxy.getAxisModel();
|
},
|
|
/**
|
* If not specified, set to undefined.
|
*
|
* @public
|
* @param {Object} opt
|
* @param {number} [opt.start]
|
* @param {number} [opt.end]
|
* @param {number} [opt.startValue]
|
* @param {number} [opt.endValue]
|
* @param {boolean} [ignoreUpdateRangeUsg=false]
|
*/
|
setRawRange: function (opt, ignoreUpdateRangeUsg) {
|
each(['start', 'end', 'startValue', 'endValue'], function (name) {
|
// If any of those prop is null/undefined, we should alos set
|
// them, because only one pair between start/end and
|
// startValue/endValue can work.
|
this.option[name] = opt[name];
|
}, this);
|
|
!ignoreUpdateRangeUsg && updateRangeUse(this, opt);
|
},
|
|
/**
|
* @public
|
* @return {Array.<number>} [startPercent, endPercent]
|
*/
|
getPercentRange: function () {
|
var axisProxy = this.findRepresentativeAxisProxy();
|
if (axisProxy) {
|
return axisProxy.getDataPercentWindow();
|
}
|
},
|
|
/**
|
* @public
|
* For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
|
*
|
* @param {string} [axisDimName]
|
* @param {number} [axisIndex]
|
* @return {Array.<number>} [startValue, endValue] value can only be '-' or finite number.
|
*/
|
getValueRange: function (axisDimName, axisIndex) {
|
if (axisDimName == null && axisIndex == null) {
|
var axisProxy = this.findRepresentativeAxisProxy();
|
if (axisProxy) {
|
return axisProxy.getDataValueWindow();
|
}
|
}
|
else {
|
return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
|
}
|
},
|
|
/**
|
* @public
|
* @return {module:echarts/component/dataZoom/AxisProxy}
|
*/
|
findRepresentativeAxisProxy: function () {
|
// Find the first hosted axisProxy
|
var axisProxies = this._axisProxies;
|
for (var key in axisProxies) {
|
if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
|
return axisProxies[key];
|
}
|
}
|
|
// If no hosted axis find not hosted axisProxy.
|
// Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
|
// and the option.start or option.end settings are different. The percentRange
|
// should follow axisProxy.
|
// (We encounter this problem in toolbox data zoom.)
|
for (var key in axisProxies) {
|
if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
|
return axisProxies[key];
|
}
|
}
|
},
|
|
/**
|
* @return {Array.<string>}
|
*/
|
getRangePropMode: function () {
|
return this._rangePropMode.slice();
|
}
|
});
|
|
function retrieveRaw(option) {
|
var ret = {};
|
each(
|
['start', 'end', 'startValue', 'endValue', 'throttle'],
|
function (name) {
|
option.hasOwnProperty(name) && (ret[name] = option[name]);
|
}
|
);
|
return ret;
|
}
|
|
function updateRangeUse(dataZoomModel, rawOption) {
|
each([['start', 'startValue'], ['end', 'endValue']], function (names, index) {
|
var rangePropMode = dataZoomModel._rangePropMode;
|
if (rawOption[names[0]] != null) {
|
rangePropMode[index] = 'percent';
|
}
|
else if (rawOption[names[1]] != null) {
|
rangePropMode[index] = 'value';
|
}
|
// else remain its original setting.
|
});
|
}
|
|
module.exports = DataZoomModel;
|
|
|
|
/***/ },
|
/* 358 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
var formatUtil = __webpack_require__(6);
|
var zrUtil = __webpack_require__(4);
|
|
var helper = {};
|
|
var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle', 'single'];
|
// Supported coords.
|
var COORDS = ['cartesian2d', 'polar', 'singleAxis'];
|
|
/**
|
* @param {string} coordType
|
* @return {boolean}
|
*/
|
helper.isCoordSupported = function (coordType) {
|
return zrUtil.indexOf(COORDS, coordType) >= 0;
|
};
|
|
/**
|
* Create "each" method to iterate names.
|
*
|
* @pubilc
|
* @param {Array.<string>} names
|
* @param {Array.<string>=} attrs
|
* @return {Function}
|
*/
|
helper.createNameEach = function (names, attrs) {
|
names = names.slice();
|
var capitalNames = zrUtil.map(names, formatUtil.capitalFirst);
|
attrs = (attrs || []).slice();
|
var capitalAttrs = zrUtil.map(attrs, formatUtil.capitalFirst);
|
|
return function (callback, context) {
|
zrUtil.each(names, function (name, index) {
|
var nameObj = {name: name, capital: capitalNames[index]};
|
|
for (var j = 0; j < attrs.length; j++) {
|
nameObj[attrs[j]] = name + capitalAttrs[j];
|
}
|
|
callback.call(context, nameObj);
|
});
|
};
|
};
|
|
/**
|
* Iterate each dimension name.
|
*
|
* @public
|
* @param {Function} callback The parameter is like:
|
* {
|
* name: 'angle',
|
* capital: 'Angle',
|
* axis: 'angleAxis',
|
* axisIndex: 'angleAixs',
|
* index: 'angleIndex'
|
* }
|
* @param {Object} context
|
*/
|
helper.eachAxisDim = helper.createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']);
|
|
/**
|
* If tow dataZoomModels has the same axis controlled, we say that they are 'linked'.
|
* dataZoomModels and 'links' make up one or more graphics.
|
* This function finds the graphic where the source dataZoomModel is in.
|
*
|
* @public
|
* @param {Function} forEachNode Node iterator.
|
* @param {Function} forEachEdgeType edgeType iterator
|
* @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id.
|
* @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}}
|
*/
|
helper.createLinkedNodesFinder = function (forEachNode, forEachEdgeType, edgeIdGetter) {
|
|
return function (sourceNode) {
|
var result = {
|
nodes: [],
|
records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean).
|
};
|
|
forEachEdgeType(function (edgeType) {
|
result.records[edgeType.name] = {};
|
});
|
|
if (!sourceNode) {
|
return result;
|
}
|
|
absorb(sourceNode, result);
|
|
var existsLink;
|
do {
|
existsLink = false;
|
forEachNode(processSingleNode);
|
}
|
while (existsLink);
|
|
function processSingleNode(node) {
|
if (!isNodeAbsorded(node, result) && isLinked(node, result)) {
|
absorb(node, result);
|
existsLink = true;
|
}
|
}
|
|
return result;
|
};
|
|
function isNodeAbsorded(node, result) {
|
return zrUtil.indexOf(result.nodes, node) >= 0;
|
}
|
|
function isLinked(node, result) {
|
var hasLink = false;
|
forEachEdgeType(function (edgeType) {
|
zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) {
|
result.records[edgeType.name][edgeId] && (hasLink = true);
|
});
|
});
|
return hasLink;
|
}
|
|
function absorb(node, result) {
|
result.nodes.push(node);
|
forEachEdgeType(function (edgeType) {
|
zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) {
|
result.records[edgeType.name][edgeId] = true;
|
});
|
});
|
}
|
};
|
|
module.exports = helper;
|
|
|
/***/ },
|
/* 359 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Axis operator
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
var helper = __webpack_require__(358);
|
var each = zrUtil.each;
|
var asc = numberUtil.asc;
|
|
/**
|
* Operate single axis.
|
* One axis can only operated by one axis operator.
|
* Different dataZoomModels may be defined to operate the same axis.
|
* (i.e. 'inside' data zoom and 'slider' data zoom components)
|
* So dataZoomModels share one axisProxy in that case.
|
*
|
* @class
|
*/
|
var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) {
|
|
/**
|
* @private
|
* @type {string}
|
*/
|
this._dimName = dimName;
|
|
/**
|
* @private
|
*/
|
this._axisIndex = axisIndex;
|
|
/**
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._valueWindow;
|
|
/**
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._percentWindow;
|
|
/**
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._dataExtent;
|
|
/**
|
* @readOnly
|
* @type {module: echarts/model/Global}
|
*/
|
this.ecModel = ecModel;
|
|
/**
|
* @private
|
* @type {module: echarts/component/dataZoom/DataZoomModel}
|
*/
|
this._dataZoomModel = dataZoomModel;
|
};
|
|
AxisProxy.prototype = {
|
|
constructor: AxisProxy,
|
|
/**
|
* Whether the axisProxy is hosted by dataZoomModel.
|
*
|
* @public
|
* @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
|
* @return {boolean}
|
*/
|
hostedBy: function (dataZoomModel) {
|
return this._dataZoomModel === dataZoomModel;
|
},
|
|
/**
|
* @return {Array.<number>} Value can only be NaN or finite value.
|
*/
|
getDataValueWindow: function () {
|
return this._valueWindow.slice();
|
},
|
|
/**
|
* @return {Array.<number>}
|
*/
|
getDataPercentWindow: function () {
|
return this._percentWindow.slice();
|
},
|
|
/**
|
* @public
|
* @param {number} axisIndex
|
* @return {Array} seriesModels
|
*/
|
getTargetSeriesModels: function () {
|
var seriesModels = [];
|
var ecModel = this.ecModel;
|
|
ecModel.eachSeries(function (seriesModel) {
|
if (helper.isCoordSupported(seriesModel.get('coordinateSystem'))) {
|
var dimName = this._dimName;
|
var axisModel = ecModel.queryComponents({
|
mainType: dimName + 'Axis',
|
index: seriesModel.get(dimName + 'AxisIndex'),
|
id: seriesModel.get(dimName + 'AxisId')
|
})[0];
|
if (this._axisIndex === (axisModel && axisModel.componentIndex)) {
|
seriesModels.push(seriesModel);
|
}
|
}
|
}, this);
|
|
return seriesModels;
|
},
|
|
getAxisModel: function () {
|
return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex);
|
},
|
|
getOtherAxisModel: function () {
|
var axisDim = this._dimName;
|
var ecModel = this.ecModel;
|
var axisModel = this.getAxisModel();
|
var isCartesian = axisDim === 'x' || axisDim === 'y';
|
var otherAxisDim;
|
var coordSysIndexName;
|
if (isCartesian) {
|
coordSysIndexName = 'gridIndex';
|
otherAxisDim = axisDim === 'x' ? 'y' : 'x';
|
}
|
else {
|
coordSysIndexName = 'polarIndex';
|
otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle';
|
}
|
var foundOtherAxisModel;
|
ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) {
|
if ((otherAxisModel.get(coordSysIndexName) || 0)
|
=== (axisModel.get(coordSysIndexName) || 0)
|
) {
|
foundOtherAxisModel = otherAxisModel;
|
}
|
});
|
return foundOtherAxisModel;
|
},
|
|
/**
|
* Only calculate by given range and this._dataExtent, do not change anything.
|
*
|
* @param {Object} opt
|
* @param {number} [opt.start]
|
* @param {number} [opt.end]
|
* @param {number} [opt.startValue]
|
* @param {number} [opt.endValue]
|
*/
|
calculateDataWindow: function (opt) {
|
var dataExtent = this._dataExtent;
|
var axisModel = this.getAxisModel();
|
var scale = axisModel.axis.scale;
|
var rangePropMode = this._dataZoomModel.getRangePropMode();
|
var percentExtent = [0, 100];
|
var percentWindow = [
|
opt.start,
|
opt.end
|
];
|
var valueWindow = [];
|
|
each(['startValue', 'endValue'], function (prop) {
|
valueWindow.push(opt[prop] != null ? scale.parse(opt[prop]) : null);
|
});
|
|
// Normalize bound.
|
each([0, 1], function (idx) {
|
var boundValue = valueWindow[idx];
|
var boundPercent = percentWindow[idx];
|
|
// Notice: dataZoom is based either on `percentProp` ('start', 'end') or
|
// on `valueProp` ('startValue', 'endValue'). The former one is suitable
|
// for cases that a dataZoom component controls multiple axes with different
|
// unit or extent, and the latter one is suitable for accurate zoom by pixel
|
// (e.g., in dataZoomSelect). `valueProp` can be calculated from `percentProp`,
|
// but it is awkward that `percentProp` can not be obtained from `valueProp`
|
// accurately (because all of values that are overflow the `dataExtent` will
|
// be calculated to percent '100%'). So we have to use
|
// `dataZoom.getRangePropMode()` to mark which prop is used.
|
// `rangePropMode` is updated only when setOption or dispatchAction, otherwise
|
// it remains its original value.
|
|
if (rangePropMode[idx] === 'percent') {
|
if (boundPercent == null) {
|
boundPercent = percentExtent[idx];
|
}
|
// Use scale.parse to math round for category or time axis.
|
boundValue = scale.parse(numberUtil.linearMap(
|
boundPercent, percentExtent, dataExtent, true
|
));
|
}
|
else {
|
// Calculating `percent` from `value` may be not accurate, because
|
// This calculation can not be inversed, because all of values that
|
// are overflow the `dataExtent` will be calculated to percent '100%'
|
boundPercent = numberUtil.linearMap(
|
boundValue, dataExtent, percentExtent, true
|
);
|
}
|
|
// valueWindow[idx] = round(boundValue);
|
// percentWindow[idx] = round(boundPercent);
|
valueWindow[idx] = boundValue;
|
percentWindow[idx] = boundPercent;
|
});
|
|
return {
|
valueWindow: asc(valueWindow),
|
percentWindow: asc(percentWindow)
|
};
|
},
|
|
/**
|
* Notice: reset should not be called before series.restoreData() called,
|
* so it is recommanded to be called in "process stage" but not "model init
|
* stage".
|
*
|
* @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
|
*/
|
reset: function (dataZoomModel) {
|
if (dataZoomModel !== this._dataZoomModel) {
|
return;
|
}
|
|
// Culculate data window and data extent, and record them.
|
this._dataExtent = calculateDataExtent(
|
this, this._dimName, this.getTargetSeriesModels()
|
);
|
|
var dataWindow = this.calculateDataWindow(dataZoomModel.option);
|
|
this._valueWindow = dataWindow.valueWindow;
|
this._percentWindow = dataWindow.percentWindow;
|
|
// Update axis setting then.
|
setAxisModel(this);
|
},
|
|
/**
|
* @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
|
*/
|
restore: function (dataZoomModel) {
|
if (dataZoomModel !== this._dataZoomModel) {
|
return;
|
}
|
|
this._valueWindow = this._percentWindow = null;
|
setAxisModel(this, true);
|
},
|
|
/**
|
* @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel
|
*/
|
filterData: function (dataZoomModel) {
|
if (dataZoomModel !== this._dataZoomModel) {
|
return;
|
}
|
|
var axisDim = this._dimName;
|
var seriesModels = this.getTargetSeriesModels();
|
var filterMode = dataZoomModel.get('filterMode');
|
var valueWindow = this._valueWindow;
|
|
// FIXME
|
// Toolbox may has dataZoom injected. And if there are stacked bar chart
|
// with NaN data, NaN will be filtered and stack will be wrong.
|
// So we need to force the mode to be set empty.
|
// In fect, it is not a big deal that do not support filterMode-'filter'
|
// when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis
|
// selection" some day, which might need "adapt to data extent on the
|
// otherAxis", which is disabled by filterMode-'empty'.
|
var otherAxisModel = this.getOtherAxisModel();
|
if (dataZoomModel.get('$fromToolbox')
|
&& otherAxisModel
|
&& otherAxisModel.get('type') === 'category'
|
) {
|
filterMode = 'empty';
|
}
|
|
// Process series data
|
each(seriesModels, function (seriesModel) {
|
var seriesData = seriesModel.getData();
|
|
seriesData && each(seriesModel.coordDimToDataDim(axisDim), function (dim) {
|
if (filterMode === 'empty') {
|
seriesModel.setData(
|
seriesData.map(dim, function (value) {
|
return !isInWindow(value) ? NaN : value;
|
})
|
);
|
}
|
else {
|
seriesData.filterSelf(dim, isInWindow);
|
}
|
});
|
});
|
|
function isInWindow(value) {
|
return value >= valueWindow[0] && value <= valueWindow[1];
|
}
|
}
|
};
|
|
function calculateDataExtent(axisProxy, axisDim, seriesModels) {
|
var dataExtent = [Infinity, -Infinity];
|
|
each(seriesModels, function (seriesModel) {
|
var seriesData = seriesModel.getData();
|
if (seriesData) {
|
each(seriesModel.coordDimToDataDim(axisDim), function (dim) {
|
var seriesExtent = seriesData.getDataExtent(dim);
|
seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]);
|
seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]);
|
});
|
}
|
});
|
|
if (dataExtent[1] < dataExtent[0]) {
|
dataExtent = [NaN, NaN];
|
}
|
|
// It is important to get "consistent" extent when more then one axes is
|
// controlled by a `dataZoom`, otherwise those axes will not be synchronized
|
// when zooming. But it is difficult to know what is "consistent", considering
|
// axes have different type or even different meanings (For example, two
|
// time axes are used to compare data of the same date in different years).
|
// So basically dataZoom just obtains extent by series.data (in category axis
|
// extent can be obtained from axis.data).
|
// Nevertheless, user can set min/max/scale on axes to make extent of axes
|
// consistent.
|
fixExtentByAxis(axisProxy, dataExtent);
|
|
return dataExtent;
|
}
|
|
function fixExtentByAxis(axisProxy, dataExtent) {
|
var axisModel = axisProxy.getAxisModel();
|
var min = axisModel.getMin(true);
|
|
// For category axis, if min/max/scale are not set, extent is determined
|
// by axis.data by default.
|
var isCategoryAxis = axisModel.get('type') === 'category';
|
var axisDataLen = isCategoryAxis && (axisModel.get('data') || []).length;
|
|
if (min != null && min !== 'dataMin') {
|
dataExtent[0] = min;
|
}
|
else if (isCategoryAxis) {
|
dataExtent[0] = axisDataLen > 0 ? 0 : NaN;
|
}
|
|
var max = axisModel.getMax(true);
|
if (max != null && max !== 'dataMax') {
|
dataExtent[1] = max;
|
}
|
else if (isCategoryAxis) {
|
dataExtent[1] = axisDataLen > 0 ? axisDataLen - 1 : NaN;
|
}
|
|
if (!axisModel.get('scale', true)) {
|
dataExtent[0] > 0 && (dataExtent[0] = 0);
|
dataExtent[1] < 0 && (dataExtent[1] = 0);
|
}
|
|
// For value axis, if min/max/scale are not set, we just use the extent obtained
|
// by series data, which may be a little different from the extent calculated by
|
// `axisHelper.getScaleExtent`. But the different just affects the experience a
|
// little when zooming. So it will not be fixed until some users require it strongly.
|
|
return dataExtent;
|
}
|
|
function setAxisModel(axisProxy, isRestore) {
|
var axisModel = axisProxy.getAxisModel();
|
|
var percentWindow = axisProxy._percentWindow;
|
var valueWindow = axisProxy._valueWindow;
|
|
if (!percentWindow) {
|
return;
|
}
|
|
// [0, 500]: arbitrary value, guess axis extent.
|
var precision = numberUtil.getPixelPrecision(valueWindow, [0, 500]);
|
// isRestore or isFull
|
var useOrigin = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100);
|
|
axisModel.setRange(
|
useOrigin ? null : +valueWindow[0].toFixed(precision),
|
useOrigin ? null : +valueWindow[1].toFixed(precision)
|
);
|
}
|
|
module.exports = AxisProxy;
|
|
|
|
/***/ },
|
/* 360 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var ComponentView = __webpack_require__(29);
|
|
module.exports = ComponentView.extend({
|
|
type: 'dataZoom',
|
|
render: function (dataZoomModel, ecModel, api, payload) {
|
this.dataZoomModel = dataZoomModel;
|
this.ecModel = ecModel;
|
this.api = api;
|
},
|
|
/**
|
* Find the first target coordinate system.
|
*
|
* @protected
|
* @return {Object} {
|
* grid: [
|
* {model: coord0, axisModels: [axis1, axis3], coordIndex: 1},
|
* {model: coord1, axisModels: [axis0, axis2], coordIndex: 0},
|
* ...
|
* ], // cartesians must not be null/undefined.
|
* polar: [
|
* {model: coord0, axisModels: [axis4], coordIndex: 0},
|
* ...
|
* ], // polars must not be null/undefined.
|
* singleAxis: [
|
* {model: coord0, axisModels: [], coordIndex: 0}
|
* ]
|
*/
|
getTargetCoordInfo: function () {
|
var dataZoomModel = this.dataZoomModel;
|
var ecModel = this.ecModel;
|
var coordSysLists = {};
|
|
dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
|
var axisModel = ecModel.getComponent(dimNames.axis, axisIndex);
|
if (axisModel) {
|
var coordModel = axisModel.getCoordSysModel();
|
coordModel && save(
|
coordModel,
|
axisModel,
|
coordSysLists[coordModel.mainType] || (coordSysLists[coordModel.mainType] = []),
|
coordModel.componentIndex
|
);
|
}
|
}, this);
|
|
function save(coordModel, axisModel, store, coordIndex) {
|
var item;
|
for (var i = 0; i < store.length; i++) {
|
if (store[i].model === coordModel) {
|
item = store[i];
|
break;
|
}
|
}
|
if (!item) {
|
store.push(item = {
|
model: coordModel, axisModels: [], coordIndex: coordIndex
|
});
|
}
|
item.axisModels.push(axisModel);
|
}
|
|
return coordSysLists;
|
}
|
|
});
|
|
|
|
/***/ },
|
/* 361 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom model
|
*/
|
|
|
var DataZoomModel = __webpack_require__(357);
|
|
var SliderZoomModel = DataZoomModel.extend({
|
|
type: 'dataZoom.slider',
|
|
layoutMode: 'box',
|
|
/**
|
* @protected
|
*/
|
defaultOption: {
|
show: true,
|
|
// ph => placeholder. Using placehoder here because
|
// deault value can only be drived in view stage.
|
right: 'ph', // Default align to grid rect.
|
top: 'ph', // Default align to grid rect.
|
width: 'ph', // Default align to grid rect.
|
height: 'ph', // Default align to grid rect.
|
left: null, // Default align to grid rect.
|
bottom: null, // Default align to grid rect.
|
|
backgroundColor: 'rgba(47,69,84,0)', // Background of slider zoom component.
|
// dataBackgroundColor: '#ddd', // Background coor of data shadow and border of box,
|
// highest priority, remain for compatibility of
|
// previous version, but not recommended any more.
|
dataBackground: {
|
lineStyle: {
|
color: '#2f4554',
|
width: 0.5,
|
opacity: 0.3
|
},
|
areaStyle: {
|
color: 'rgba(47,69,84,0.3)',
|
opacity: 0.3
|
}
|
},
|
borderColor: '#ddd', // border color of the box. For compatibility,
|
// if dataBackgroundColor is set, borderColor
|
// is ignored.
|
|
fillerColor: 'rgba(167,183,204,0.4)', // Color of selected area.
|
// handleColor: 'rgba(89,170,216,0.95)', // Color of handle.
|
// handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.6,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z',
|
handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z',
|
// Percent of the slider height
|
handleSize: '100%',
|
|
handleStyle: {
|
color: '#a7b7cc'
|
},
|
|
labelPrecision: null,
|
labelFormatter: null,
|
showDetail: true,
|
showDataShadow: 'auto', // Default auto decision.
|
realtime: true,
|
zoomLock: false, // Whether disable zoom.
|
textStyle: {
|
color: '#333'
|
}
|
}
|
|
});
|
|
module.exports = SliderZoomModel;
|
|
|
|
/***/ },
|
/* 362 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var throttle = __webpack_require__(81);
|
var DataZoomView = __webpack_require__(360);
|
var Rect = graphic.Rect;
|
var numberUtil = __webpack_require__(7);
|
var linearMap = numberUtil.linearMap;
|
var layout = __webpack_require__(21);
|
var sliderMove = __webpack_require__(238);
|
var eventTool = __webpack_require__(88);
|
|
var asc = numberUtil.asc;
|
var bind = zrUtil.bind;
|
// var mathMax = Math.max;
|
var each = zrUtil.each;
|
|
// Constants
|
var DEFAULT_LOCATION_EDGE_GAP = 7;
|
var DEFAULT_FRAME_BORDER_WIDTH = 1;
|
var DEFAULT_FILLER_SIZE = 30;
|
var HORIZONTAL = 'horizontal';
|
var VERTICAL = 'vertical';
|
var LABEL_GAP = 5;
|
var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter'];
|
|
var SliderZoomView = DataZoomView.extend({
|
|
type: 'dataZoom.slider',
|
|
init: function (ecModel, api) {
|
|
/**
|
* @private
|
* @type {Object}
|
*/
|
this._displayables = {};
|
|
/**
|
* @private
|
* @type {string}
|
*/
|
this._orient;
|
|
/**
|
* [0, 100]
|
* @private
|
*/
|
this._range;
|
|
/**
|
* [coord of the first handle, coord of the second handle]
|
* @private
|
*/
|
this._handleEnds;
|
|
/**
|
* [length, thick]
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._size;
|
|
/**
|
* @private
|
* @type {number}
|
*/
|
this._handleWidth;
|
|
/**
|
* @private
|
* @type {number}
|
*/
|
this._handleHeight;
|
|
/**
|
* @private
|
*/
|
this._location;
|
|
/**
|
* @private
|
*/
|
this._dragging;
|
|
/**
|
* @private
|
*/
|
this._dataShadowInfo;
|
|
this.api = api;
|
},
|
|
/**
|
* @override
|
*/
|
render: function (dataZoomModel, ecModel, api, payload) {
|
SliderZoomView.superApply(this, 'render', arguments);
|
|
throttle.createOrUpdate(
|
this,
|
'_dispatchZoomAction',
|
this.dataZoomModel.get('throttle'),
|
'fixRate'
|
);
|
|
this._orient = dataZoomModel.get('orient');
|
|
if (this.dataZoomModel.get('show') === false) {
|
this.group.removeAll();
|
return;
|
}
|
|
// Notice: this._resetInterval() should not be executed when payload.type
|
// is 'dataZoom', origin this._range should be maintained, otherwise 'pan'
|
// or 'zoom' info will be missed because of 'throttle' of this.dispatchAction,
|
if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) {
|
this._buildView();
|
}
|
|
this._updateView();
|
},
|
|
/**
|
* @override
|
*/
|
remove: function () {
|
SliderZoomView.superApply(this, 'remove', arguments);
|
throttle.clear(this, '_dispatchZoomAction');
|
},
|
|
/**
|
* @override
|
*/
|
dispose: function () {
|
SliderZoomView.superApply(this, 'dispose', arguments);
|
throttle.clear(this, '_dispatchZoomAction');
|
},
|
|
_buildView: function () {
|
var thisGroup = this.group;
|
|
thisGroup.removeAll();
|
|
this._resetLocation();
|
this._resetInterval();
|
|
var barGroup = this._displayables.barGroup = new graphic.Group();
|
|
this._renderBackground();
|
|
this._renderHandle();
|
|
this._renderDataShadow();
|
|
thisGroup.add(barGroup);
|
|
this._positionGroup();
|
},
|
|
/**
|
* @private
|
*/
|
_resetLocation: function () {
|
var dataZoomModel = this.dataZoomModel;
|
var api = this.api;
|
|
// If some of x/y/width/height are not specified,
|
// auto-adapt according to target grid.
|
var coordRect = this._findCoordRect();
|
var ecSize = {width: api.getWidth(), height: api.getHeight()};
|
// Default align by coordinate system rect.
|
var positionInfo = this._orient === HORIZONTAL
|
? {
|
// Why using 'right', because right should be used in vertical,
|
// and it is better to be consistent for dealing with position param merge.
|
right: ecSize.width - coordRect.x - coordRect.width,
|
top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP),
|
width: coordRect.width,
|
height: DEFAULT_FILLER_SIZE
|
}
|
: { // vertical
|
right: DEFAULT_LOCATION_EDGE_GAP,
|
top: coordRect.y,
|
width: DEFAULT_FILLER_SIZE,
|
height: coordRect.height
|
};
|
|
// Do not write back to option and replace value 'ph', because
|
// the 'ph' value should be recalculated when resize.
|
var layoutParams = layout.getLayoutParams(dataZoomModel.option);
|
|
// Replace the placeholder value.
|
zrUtil.each(['right', 'top', 'width', 'height'], function (name) {
|
if (layoutParams[name] === 'ph') {
|
layoutParams[name] = positionInfo[name];
|
}
|
});
|
|
var layoutRect = layout.getLayoutRect(
|
layoutParams,
|
ecSize,
|
dataZoomModel.padding
|
);
|
|
this._location = {x: layoutRect.x, y: layoutRect.y};
|
this._size = [layoutRect.width, layoutRect.height];
|
this._orient === VERTICAL && this._size.reverse();
|
},
|
|
/**
|
* @private
|
*/
|
_positionGroup: function () {
|
var thisGroup = this.group;
|
var location = this._location;
|
var orient = this._orient;
|
|
// Just use the first axis to determine mapping.
|
var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel();
|
var inverse = targetAxisModel && targetAxisModel.get('inverse');
|
|
var barGroup = this._displayables.barGroup;
|
var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse;
|
|
// Transform barGroup.
|
barGroup.attr(
|
(orient === HORIZONTAL && !inverse)
|
? {scale: otherAxisInverse ? [1, 1] : [1, -1]}
|
: (orient === HORIZONTAL && inverse)
|
? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]}
|
: (orient === VERTICAL && !inverse)
|
? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2}
|
// Dont use Math.PI, considering shadow direction.
|
: {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2}
|
);
|
|
// Position barGroup
|
var rect = thisGroup.getBoundingRect([barGroup]);
|
thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]);
|
},
|
|
/**
|
* @private
|
*/
|
_getViewExtent: function () {
|
return [0, this._size[0]];
|
},
|
|
_renderBackground : function () {
|
var dataZoomModel = this.dataZoomModel;
|
var size = this._size;
|
|
this._displayables.barGroup.add(new Rect({
|
silent: true,
|
shape: {
|
x: 0, y: 0, width: size[0], height: size[1]
|
},
|
style: {
|
fill: dataZoomModel.get('backgroundColor')
|
},
|
z2: -40
|
}));
|
},
|
|
_renderDataShadow: function () {
|
var info = this._dataShadowInfo = this._prepareDataShadowInfo();
|
|
if (!info) {
|
return;
|
}
|
|
var size = this._size;
|
var seriesModel = info.series;
|
var data = seriesModel.getRawData();
|
var otherDim = seriesModel.getShadowDim
|
? seriesModel.getShadowDim() // @see candlestick
|
: info.otherDim;
|
|
if (otherDim == null) {
|
return;
|
}
|
|
var otherDataExtent = data.getDataExtent(otherDim);
|
// Nice extent.
|
var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3;
|
otherDataExtent = [
|
otherDataExtent[0] - otherOffset,
|
otherDataExtent[1] + otherOffset
|
];
|
var otherShadowExtent = [0, size[1]];
|
|
var thisShadowExtent = [0, size[0]];
|
|
var areaPoints = [[size[0], 0], [0, 0]];
|
var linePoints = [];
|
var step = thisShadowExtent[1] / (data.count() - 1);
|
var thisCoord = 0;
|
|
// Optimize for large data shadow
|
var stride = Math.round(data.count() / size[0]);
|
var lastIsEmpty;
|
data.each([otherDim], function (value, index) {
|
if (stride > 0 && (index % stride)) {
|
thisCoord += step;
|
return;
|
}
|
|
// FIXME
|
// Should consider axis.min/axis.max when drawing dataShadow.
|
|
// FIXME
|
// 应该使用统一的空判断?还是在list里进行空判断?
|
var isEmpty = value == null || isNaN(value) || value === '';
|
// See #4235.
|
var otherCoord = isEmpty
|
? 0 : linearMap(value, otherDataExtent, otherShadowExtent, true);
|
|
// Attempt to draw data shadow precisely when there are empty value.
|
if (isEmpty && !lastIsEmpty && index) {
|
areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]);
|
linePoints.push([linePoints[linePoints.length - 1][0], 0]);
|
}
|
else if (!isEmpty && lastIsEmpty) {
|
areaPoints.push([thisCoord, 0]);
|
linePoints.push([thisCoord, 0]);
|
}
|
|
areaPoints.push([thisCoord, otherCoord]);
|
linePoints.push([thisCoord, otherCoord]);
|
|
thisCoord += step;
|
lastIsEmpty = isEmpty;
|
});
|
|
var dataZoomModel = this.dataZoomModel;
|
// var dataBackgroundModel = dataZoomModel.getModel('dataBackground');
|
this._displayables.barGroup.add(new graphic.Polygon({
|
shape: {points: areaPoints},
|
style: zrUtil.defaults(
|
{fill: dataZoomModel.get('dataBackgroundColor')},
|
dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle()
|
),
|
silent: true,
|
z2: -20
|
}));
|
this._displayables.barGroup.add(new graphic.Polyline({
|
shape: {points: linePoints},
|
style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(),
|
silent: true,
|
z2: -19
|
}));
|
},
|
|
_prepareDataShadowInfo: function () {
|
var dataZoomModel = this.dataZoomModel;
|
var showDataShadow = dataZoomModel.get('showDataShadow');
|
|
if (showDataShadow === false) {
|
return;
|
}
|
|
// Find a representative series.
|
var result;
|
var ecModel = this.ecModel;
|
|
dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) {
|
var seriesModels = dataZoomModel
|
.getAxisProxy(dimNames.name, axisIndex)
|
.getTargetSeriesModels();
|
|
zrUtil.each(seriesModels, function (seriesModel) {
|
if (result) {
|
return;
|
}
|
|
if (showDataShadow !== true && zrUtil.indexOf(
|
SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')
|
) < 0
|
) {
|
return;
|
}
|
|
var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis;
|
var otherDim = getOtherDim(dimNames.name);
|
var otherAxisInverse;
|
var coordSys = seriesModel.coordinateSystem;
|
if (otherDim != null && coordSys.getOtherAxis) {
|
otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse;
|
}
|
|
result = {
|
thisAxis: thisAxis,
|
series: seriesModel,
|
thisDim: dimNames.name,
|
otherDim: otherDim,
|
otherAxisInverse: otherAxisInverse
|
};
|
|
}, this);
|
|
}, this);
|
|
return result;
|
},
|
|
_renderHandle: function () {
|
var displaybles = this._displayables;
|
var handles = displaybles.handles = [];
|
var handleLabels = displaybles.handleLabels = [];
|
var barGroup = this._displayables.barGroup;
|
var size = this._size;
|
var dataZoomModel = this.dataZoomModel;
|
|
barGroup.add(displaybles.filler = new Rect({
|
draggable: true,
|
cursor: 'move',
|
drift: bind(this._onDragMove, this, 'all'),
|
onmousemove: function (e) {
|
// Fot mobile devicem, prevent screen slider on the button.
|
eventTool.stop(e.event);
|
},
|
ondragstart: bind(this._showDataInfo, this, true),
|
ondragend: bind(this._onDragEnd, this),
|
onmouseover: bind(this._showDataInfo, this, true),
|
onmouseout: bind(this._showDataInfo, this, false),
|
style: {
|
fill: dataZoomModel.get('fillerColor'),
|
textPosition : 'inside'
|
}
|
}));
|
|
// Frame border.
|
barGroup.add(new Rect(graphic.subPixelOptimizeRect({
|
silent: true,
|
shape: {
|
x: 0,
|
y: 0,
|
width: size[0],
|
height: size[1]
|
},
|
style: {
|
stroke: dataZoomModel.get('dataBackgroundColor')
|
|| dataZoomModel.get('borderColor'),
|
lineWidth: DEFAULT_FRAME_BORDER_WIDTH,
|
fill: 'rgba(0,0,0,0)'
|
}
|
})));
|
|
var iconStr = dataZoomModel.get('handleIcon');
|
each([0, 1], function (handleIndex) {
|
var path = graphic.makePath(iconStr, {
|
style: {
|
strokeNoScale: true
|
},
|
rectHover: true,
|
cursor: this._orient === 'vertical' ? 'ns-resize' : 'ew-resize',
|
draggable: true,
|
drift: bind(this._onDragMove, this, handleIndex),
|
onmousemove: function (e) {
|
// Fot mobile devicem, prevent screen slider on the button.
|
eventTool.stop(e.event);
|
},
|
ondragend: bind(this._onDragEnd, this),
|
onmouseover: bind(this._showDataInfo, this, true),
|
onmouseout: bind(this._showDataInfo, this, false)
|
}, {
|
x: -0.5,
|
y: 0,
|
width: 1,
|
height: 1
|
}, 'center');
|
|
var bRect = path.getBoundingRect();
|
this._handleHeight = numberUtil.parsePercent(dataZoomModel.get('handleSize'), this._size[1]);
|
this._handleWidth = bRect.width / bRect.height * this._handleHeight;
|
|
path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle());
|
var handleColor = dataZoomModel.get('handleColor');
|
// Compatitable with previous version
|
if (handleColor != null) {
|
path.style.fill = handleColor;
|
}
|
|
barGroup.add(handles[handleIndex] = path);
|
|
var textStyleModel = dataZoomModel.textStyleModel;
|
|
this.group.add(
|
handleLabels[handleIndex] = new graphic.Text({
|
silent: true,
|
invisible: true,
|
style: {
|
x: 0, y: 0, text: '',
|
textVerticalAlign: 'middle',
|
textAlign: 'center',
|
fill: textStyleModel.getTextColor(),
|
textFont: textStyleModel.getFont()
|
},
|
z2: 10
|
}));
|
|
}, this);
|
},
|
|
/**
|
* @private
|
*/
|
_resetInterval: function () {
|
var range = this._range = this.dataZoomModel.getPercentRange();
|
var viewExtent = this._getViewExtent();
|
|
this._handleEnds = [
|
linearMap(range[0], [0, 100], viewExtent, true),
|
linearMap(range[1], [0, 100], viewExtent, true)
|
];
|
},
|
|
/**
|
* @private
|
* @param {(number|string)} handleIndex 0 or 1 or 'all'
|
* @param {number} dx
|
* @param {number} dy
|
*/
|
_updateInterval: function (handleIndex, delta) {
|
var handleEnds = this._handleEnds;
|
var viewExtend = this._getViewExtent();
|
|
sliderMove(
|
delta,
|
handleEnds,
|
viewExtend,
|
(handleIndex === 'all' || this.dataZoomModel.get('zoomLock'))
|
? 'rigid' : 'cross',
|
handleIndex
|
);
|
|
this._range = asc([
|
linearMap(handleEnds[0], viewExtend, [0, 100], true),
|
linearMap(handleEnds[1], viewExtend, [0, 100], true)
|
]);
|
},
|
|
/**
|
* @private
|
*/
|
_updateView: function (nonRealtime) {
|
var displaybles = this._displayables;
|
var handleEnds = this._handleEnds;
|
var handleInterval = asc(handleEnds.slice());
|
var size = this._size;
|
|
each([0, 1], function (handleIndex) {
|
// Handles
|
var handle = displaybles.handles[handleIndex];
|
var handleHeight = this._handleHeight;
|
handle.attr({
|
scale: [handleHeight, handleHeight],
|
position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2]
|
});
|
}, this);
|
|
// Filler
|
displaybles.filler.setShape({
|
x: handleInterval[0],
|
y: 0,
|
width: handleInterval[1] - handleInterval[0],
|
height: size[1]
|
});
|
|
this._updateDataInfo(nonRealtime);
|
},
|
|
/**
|
* @private
|
*/
|
_updateDataInfo: function (nonRealtime) {
|
var dataZoomModel = this.dataZoomModel;
|
var displaybles = this._displayables;
|
var handleLabels = displaybles.handleLabels;
|
var orient = this._orient;
|
var labelTexts = ['', ''];
|
|
// FIXME
|
// date型,支持formatter,autoformatter(ec2 date.getAutoFormatter)
|
if (dataZoomModel.get('showDetail')) {
|
var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
|
|
if (axisProxy) {
|
var axis = axisProxy.getAxisModel().axis;
|
var range = this._range;
|
|
var dataInterval = nonRealtime
|
// See #4434, data and axis are not processed and reset yet in non-realtime mode.
|
? axisProxy.calculateDataWindow({
|
start: range[0], end: range[1]
|
}).valueWindow
|
: axisProxy.getDataValueWindow();
|
|
labelTexts = [
|
this._formatLabel(dataInterval[0], axis),
|
this._formatLabel(dataInterval[1], axis)
|
];
|
}
|
}
|
|
var orderedHandleEnds = asc(this._handleEnds.slice());
|
|
setLabel.call(this, 0);
|
setLabel.call(this, 1);
|
|
function setLabel(handleIndex) {
|
// Label
|
// Text should not transform by barGroup.
|
// Ignore handlers transform
|
var barTransform = graphic.getTransform(
|
displaybles.handles[handleIndex].parent, this.group
|
);
|
var direction = graphic.transformDirection(
|
handleIndex === 0 ? 'right' : 'left', barTransform
|
);
|
var offset = this._handleWidth / 2 + LABEL_GAP;
|
var textPoint = graphic.applyTransform(
|
[
|
orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset),
|
this._size[1] / 2
|
],
|
barTransform
|
);
|
handleLabels[handleIndex].setStyle({
|
x: textPoint[0],
|
y: textPoint[1],
|
textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction,
|
textAlign: orient === HORIZONTAL ? direction : 'center',
|
text: labelTexts[handleIndex]
|
});
|
}
|
},
|
|
/**
|
* @private
|
*/
|
_formatLabel: function (value, axis) {
|
var dataZoomModel = this.dataZoomModel;
|
var labelFormatter = dataZoomModel.get('labelFormatter');
|
|
var labelPrecision = dataZoomModel.get('labelPrecision');
|
if (labelPrecision == null || labelPrecision === 'auto') {
|
labelPrecision = axis.getPixelPrecision();
|
}
|
|
var valueStr = (value == null || isNaN(value))
|
? ''
|
// FIXME Glue code
|
: (axis.type === 'category' || axis.type === 'time')
|
? axis.scale.getLabel(Math.round(value))
|
// param of toFixed should less then 20.
|
: value.toFixed(Math.min(labelPrecision, 20));
|
|
return zrUtil.isFunction(labelFormatter)
|
? labelFormatter(value, valueStr)
|
: zrUtil.isString(labelFormatter)
|
? labelFormatter.replace('{value}', valueStr)
|
: valueStr;
|
},
|
|
/**
|
* @private
|
* @param {boolean} showOrHide true: show, false: hide
|
*/
|
_showDataInfo: function (showOrHide) {
|
// Always show when drgging.
|
showOrHide = this._dragging || showOrHide;
|
|
var handleLabels = this._displayables.handleLabels;
|
handleLabels[0].attr('invisible', !showOrHide);
|
handleLabels[1].attr('invisible', !showOrHide);
|
},
|
|
_onDragMove: function (handleIndex, dx, dy) {
|
this._dragging = true;
|
|
// Transform dx, dy to bar coordination.
|
var vertex = this._applyBarTransform([dx, dy], true);
|
|
this._updateInterval(handleIndex, vertex[0]);
|
|
var realtime = this.dataZoomModel.get('realtime');
|
|
this._updateView(!realtime);
|
|
if (realtime) {
|
realtime && this._dispatchZoomAction();
|
}
|
},
|
|
_onDragEnd: function () {
|
this._dragging = false;
|
this._showDataInfo(false);
|
this._dispatchZoomAction();
|
},
|
|
/**
|
* This action will be throttled.
|
* @private
|
*/
|
_dispatchZoomAction: function () {
|
var range = this._range;
|
|
this.api.dispatchAction({
|
type: 'dataZoom',
|
from: this.uid,
|
dataZoomId: this.dataZoomModel.id,
|
start: range[0],
|
end: range[1]
|
});
|
},
|
|
/**
|
* @private
|
*/
|
_applyBarTransform: function (vertex, inverse) {
|
var barTransform = this._displayables.barGroup.getLocalTransform();
|
return graphic.applyTransform(vertex, barTransform, inverse);
|
},
|
|
/**
|
* @private
|
*/
|
_findCoordRect: function () {
|
// Find the grid coresponding to the first axis referred by dataZoom.
|
var rect;
|
each(this.getTargetCoordInfo(), function (coordInfoList) {
|
if (!rect && coordInfoList.length) {
|
var coordSys = coordInfoList[0].model.coordinateSystem;
|
rect = coordSys.getRect && coordSys.getRect();
|
}
|
});
|
if (!rect) {
|
var width = this.api.getWidth();
|
var height = this.api.getHeight();
|
rect = {
|
x: width * 0.2,
|
y: height * 0.2,
|
width: width * 0.6,
|
height: height * 0.6
|
};
|
}
|
|
return rect;
|
}
|
|
});
|
|
function getOtherDim(thisDim) {
|
// FIXME
|
// 这个逻辑和getOtherAxis里一致,但是写在这里是否不好
|
var map = {x: 'y', y: 'x', radius: 'angle', angle: 'radius'};
|
return map[thisDim];
|
}
|
|
module.exports = SliderZoomView;
|
|
|
|
/***/ },
|
/* 363 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom model
|
*/
|
|
|
module.exports = __webpack_require__(357).extend({
|
|
type: 'dataZoom.inside',
|
|
/**
|
* @protected
|
*/
|
defaultOption: {
|
disabled: false, // Whether disable this inside zoom.
|
zoomLock: false // Whether disable zoom but only pan.
|
}
|
});
|
|
|
/***/ },
|
/* 364 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var DataZoomView = __webpack_require__(360);
|
var zrUtil = __webpack_require__(4);
|
var sliderMove = __webpack_require__(238);
|
var roams = __webpack_require__(365);
|
var bind = zrUtil.bind;
|
|
var InsideZoomView = DataZoomView.extend({
|
|
type: 'dataZoom.inside',
|
|
/**
|
* @override
|
*/
|
init: function (ecModel, api) {
|
/**
|
* 'throttle' is used in this.dispatchAction, so we save range
|
* to avoid missing some 'pan' info.
|
* @private
|
* @type {Array.<number>}
|
*/
|
this._range;
|
},
|
|
/**
|
* @override
|
*/
|
render: function (dataZoomModel, ecModel, api, payload) {
|
InsideZoomView.superApply(this, 'render', arguments);
|
|
// Notice: origin this._range should be maintained, and should not be re-fetched
|
// from dataZoomModel when payload.type is 'dataZoom', otherwise 'pan' or 'zoom'
|
// info will be missed because of 'throttle' of this.dispatchAction.
|
if (roams.shouldRecordRange(payload, dataZoomModel.id)) {
|
this._range = dataZoomModel.getPercentRange();
|
}
|
|
// Reset controllers.
|
zrUtil.each(this.getTargetCoordInfo(), function (coordInfoList, coordSysName) {
|
|
var allCoordIds = zrUtil.map(coordInfoList, function (coordInfo) {
|
return roams.generateCoordId(coordInfo.model);
|
});
|
|
zrUtil.each(coordInfoList, function (coordInfo) {
|
var coordModel = coordInfo.model;
|
|
roams.register(
|
api,
|
{
|
coordId: roams.generateCoordId(coordModel),
|
allCoordIds: allCoordIds,
|
containsPoint: function (e, x, y) {
|
return coordModel.coordinateSystem.containPoint([x, y]);
|
},
|
dataZoomId: dataZoomModel.id,
|
throttleRate: dataZoomModel.get('throttle', true),
|
panGetRange: bind(this._onPan, this, coordInfo, coordSysName),
|
zoomGetRange: bind(this._onZoom, this, coordInfo, coordSysName)
|
}
|
);
|
}, this);
|
|
}, this);
|
},
|
|
/**
|
* @override
|
*/
|
dispose: function () {
|
roams.unregister(this.api, this.dataZoomModel.id);
|
InsideZoomView.superApply(this, 'dispose', arguments);
|
this._range = null;
|
},
|
|
/**
|
* @private
|
*/
|
_onPan: function (coordInfo, coordSysName, controller, dx, dy, oldX, oldY, newX, newY) {
|
if (this.dataZoomModel.option.disabled) {
|
return this._range;
|
}
|
|
var range = this._range.slice();
|
|
// Calculate transform by the first axis.
|
var axisModel = coordInfo.axisModels[0];
|
if (!axisModel) {
|
return;
|
}
|
|
var directionInfo = getDirectionInfo[coordSysName](
|
[oldX, oldY], [newX, newY], axisModel, controller, coordInfo
|
);
|
|
var percentDelta = directionInfo.signal
|
* (range[1] - range[0])
|
* directionInfo.pixel / directionInfo.pixelLength;
|
|
sliderMove(percentDelta, range, [0, 100], 'rigid');
|
|
return (this._range = range);
|
},
|
|
/**
|
* @private
|
*/
|
_onZoom: function (coordInfo, coordSysName, controller, scale, mouseX, mouseY) {
|
var option = this.dataZoomModel.option;
|
|
if (option.disabled || option.zoomLock) {
|
return this._range;
|
}
|
|
var range = this._range.slice();
|
|
// Calculate transform by the first axis.
|
var axisModel = coordInfo.axisModels[0];
|
if (!axisModel) {
|
return;
|
}
|
|
var directionInfo = getDirectionInfo[coordSysName](
|
null, [mouseX, mouseY], axisModel, controller, coordInfo
|
);
|
|
var percentPoint = (directionInfo.pixel - directionInfo.pixelStart) /
|
directionInfo.pixelLength * (range[1] - range[0]) + range[0];
|
|
scale = Math.max(1 / scale, 0);
|
range[0] = (range[0] - percentPoint) * scale + percentPoint;
|
range[1] = (range[1] - percentPoint) * scale + percentPoint;
|
return (this._range = fixRange(range));
|
}
|
|
});
|
|
var getDirectionInfo = {
|
|
grid: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
|
var axis = axisModel.axis;
|
var ret = {};
|
var rect = coordInfo.model.coordinateSystem.getRect();
|
oldPoint = oldPoint || [0, 0];
|
|
if (axis.dim === 'x') {
|
ret.pixel = newPoint[0] - oldPoint[0];
|
ret.pixelLength = rect.width;
|
ret.pixelStart = rect.x;
|
ret.signal = axis.inverse ? 1 : -1;
|
}
|
else { // axis.dim === 'y'
|
ret.pixel = newPoint[1] - oldPoint[1];
|
ret.pixelLength = rect.height;
|
ret.pixelStart = rect.y;
|
ret.signal = axis.inverse ? -1 : 1;
|
}
|
|
return ret;
|
},
|
|
polar: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
|
var axis = axisModel.axis;
|
var ret = {};
|
var polar = coordInfo.model.coordinateSystem;
|
var radiusExtent = polar.getRadiusAxis().getExtent();
|
var angleExtent = polar.getAngleAxis().getExtent();
|
|
oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0];
|
newPoint = polar.pointToCoord(newPoint);
|
|
if (axisModel.mainType === 'radiusAxis') {
|
ret.pixel = newPoint[0] - oldPoint[0];
|
// ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]);
|
// ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]);
|
ret.pixelLength = radiusExtent[1] - radiusExtent[0];
|
ret.pixelStart = radiusExtent[0];
|
ret.signal = axis.inverse ? 1 : -1;
|
}
|
else { // 'angleAxis'
|
ret.pixel = newPoint[1] - oldPoint[1];
|
// ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]);
|
// ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]);
|
ret.pixelLength = angleExtent[1] - angleExtent[0];
|
ret.pixelStart = angleExtent[0];
|
ret.signal = axis.inverse ? -1 : 1;
|
}
|
|
return ret;
|
},
|
|
singleAxis: function (oldPoint, newPoint, axisModel, controller, coordInfo) {
|
var axis = axisModel.axis;
|
var rect = coordInfo.model.coordinateSystem.getRect();
|
var ret = {};
|
|
oldPoint = oldPoint || [0, 0];
|
|
if (axis.orient === 'horizontal') {
|
ret.pixel = newPoint[0] - oldPoint[0];
|
ret.pixelLength = rect.width;
|
ret.pixelStart = rect.x;
|
ret.signal = axis.inverse ? 1 : -1;
|
}
|
else { // 'vertical'
|
ret.pixel = newPoint[1] - oldPoint[1];
|
ret.pixelLength = rect.height;
|
ret.pixelStart = rect.y;
|
ret.signal = axis.inverse ? -1 : 1;
|
}
|
|
return ret;
|
}
|
};
|
|
function fixRange(range) {
|
// Clamp, using !(<= or >=) to handle NaN.
|
// jshint ignore:start
|
var bound = [0, 100];
|
!(range[0] <= bound[1]) && (range[0] = bound[1]);
|
!(range[1] <= bound[1]) && (range[1] = bound[1]);
|
!(range[0] >= bound[0]) && (range[0] = bound[0]);
|
!(range[1] >= bound[0]) && (range[1] = bound[0]);
|
// jshint ignore:end
|
|
return range;
|
}
|
|
module.exports = InsideZoomView;
|
|
|
/***/ },
|
/* 365 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Roam controller manager.
|
*/
|
|
|
// Only create one roam controller for each coordinate system.
|
// one roam controller might be refered by two inside data zoom
|
// components (for example, one for x and one for y). When user
|
// pan or zoom, only dispatch one action for those data zoom
|
// components.
|
|
var zrUtil = __webpack_require__(4);
|
var RoamController = __webpack_require__(182);
|
var throttle = __webpack_require__(81);
|
var curry = zrUtil.curry;
|
|
var ATTR = '\0_ec_dataZoom_roams';
|
|
var roams = {
|
|
/**
|
* @public
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {Object} dataZoomInfo
|
* @param {string} dataZoomInfo.coordId
|
* @param {Function} dataZoomInfo.containsPoint
|
* @param {Array.<string>} dataZoomInfo.allCoordIds
|
* @param {string} dataZoomInfo.dataZoomId
|
* @param {number} dataZoomInfo.throttleRate
|
* @param {Function} dataZoomInfo.panGetRange
|
* @param {Function} dataZoomInfo.zoomGetRange
|
*/
|
register: function (api, dataZoomInfo) {
|
var store = giveStore(api);
|
var theDataZoomId = dataZoomInfo.dataZoomId;
|
var theCoordId = dataZoomInfo.coordId;
|
|
// Do clean when a dataZoom changes its target coordnate system.
|
// Avoid memory leak, dispose all not-used-registered.
|
zrUtil.each(store, function (record, coordId) {
|
var dataZoomInfos = record.dataZoomInfos;
|
if (dataZoomInfos[theDataZoomId]
|
&& zrUtil.indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0
|
) {
|
delete dataZoomInfos[theDataZoomId];
|
record.count--;
|
}
|
});
|
|
cleanStore(store);
|
|
var record = store[theCoordId];
|
// Create if needed.
|
if (!record) {
|
record = store[theCoordId] = {
|
coordId: theCoordId,
|
dataZoomInfos: {},
|
count: 0
|
};
|
record.controller = createController(api, dataZoomInfo, record);
|
record.dispatchAction = zrUtil.curry(dispatchAction, api);
|
}
|
|
// Consider resize, area should be always updated.
|
record.controller.setPointerChecker(dataZoomInfo.containsPoint);
|
|
// Update throttle.
|
throttle.createOrUpdate(
|
record,
|
'dispatchAction',
|
dataZoomInfo.throttleRate,
|
'fixRate'
|
);
|
|
// Update reference of dataZoom.
|
!(record.dataZoomInfos[theDataZoomId]) && record.count++;
|
record.dataZoomInfos[theDataZoomId] = dataZoomInfo;
|
},
|
|
/**
|
* @public
|
* @param {module:echarts/ExtensionAPI} api
|
* @param {string} dataZoomId
|
*/
|
unregister: function (api, dataZoomId) {
|
var store = giveStore(api);
|
|
zrUtil.each(store, function (record) {
|
record.controller.dispose();
|
var dataZoomInfos = record.dataZoomInfos;
|
if (dataZoomInfos[dataZoomId]) {
|
delete dataZoomInfos[dataZoomId];
|
record.count--;
|
}
|
});
|
|
cleanStore(store);
|
},
|
|
/**
|
* @public
|
*/
|
shouldRecordRange: function (payload, dataZoomId) {
|
if (payload && payload.type === 'dataZoom' && payload.batch) {
|
for (var i = 0, len = payload.batch.length; i < len; i++) {
|
if (payload.batch[i].dataZoomId === dataZoomId) {
|
return false;
|
}
|
}
|
}
|
return true;
|
},
|
|
/**
|
* @public
|
*/
|
generateCoordId: function (coordModel) {
|
return coordModel.type + '\0_' + coordModel.id;
|
}
|
};
|
|
/**
|
* Key: coordId, value: {dataZoomInfos: [], count, controller}
|
* @type {Array.<Object>}
|
*/
|
function giveStore(api) {
|
// Mount store on zrender instance, so that we do not
|
// need to worry about dispose.
|
var zr = api.getZr();
|
return zr[ATTR] || (zr[ATTR] = {});
|
}
|
|
function createController(api, dataZoomInfo, newRecord) {
|
var controller = new RoamController(api.getZr());
|
controller.enable();
|
controller.on('pan', curry(onPan, newRecord));
|
controller.on('zoom', curry(onZoom, newRecord));
|
|
return controller;
|
}
|
|
function cleanStore(store) {
|
zrUtil.each(store, function (record, coordId) {
|
if (!record.count) {
|
record.controller.dispose();
|
delete store[coordId];
|
}
|
});
|
}
|
|
function onPan(record, dx, dy, oldX, oldY, newX, newY) {
|
wrapAndDispatch(record, function (info) {
|
return info.panGetRange(record.controller, dx, dy, oldX, oldY, newX, newY);
|
});
|
}
|
|
function onZoom(record, scale, mouseX, mouseY) {
|
wrapAndDispatch(record, function (info) {
|
return info.zoomGetRange(record.controller, scale, mouseX, mouseY);
|
});
|
}
|
|
function wrapAndDispatch(record, getRange) {
|
var batch = [];
|
|
zrUtil.each(record.dataZoomInfos, function (info) {
|
var range = getRange(info);
|
range && batch.push({
|
dataZoomId: info.dataZoomId,
|
start: range[0],
|
end: range[1]
|
});
|
});
|
|
record.dispatchAction(batch);
|
}
|
|
/**
|
* This action will be throttled.
|
*/
|
function dispatchAction(api, batch) {
|
api.dispatchAction({
|
type: 'dataZoom',
|
batch: batch
|
});
|
}
|
|
module.exports = roams;
|
|
|
|
/***/ },
|
/* 366 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom processor
|
*/
|
|
|
var echarts = __webpack_require__(1);
|
|
echarts.registerProcessor(function (ecModel, api) {
|
|
ecModel.eachComponent('dataZoom', function (dataZoomModel) {
|
// We calculate window and reset axis here but not in model
|
// init stage and not after action dispatch handler, because
|
// reset should be called after seriesData.restoreData.
|
dataZoomModel.eachTargetAxis(resetSingleAxis);
|
|
// Caution: data zoom filtering is order sensitive when using
|
// percent range and no min/max/scale set on axis.
|
// For example, we have dataZoom definition:
|
// [
|
// {xAxisIndex: 0, start: 30, end: 70},
|
// {yAxisIndex: 0, start: 20, end: 80}
|
// ]
|
// In this case, [20, 80] of y-dataZoom should be based on data
|
// that have filtered by x-dataZoom using range of [30, 70],
|
// but should not be based on full raw data. Thus sliding
|
// x-dataZoom will change both ranges of xAxis and yAxis,
|
// while sliding y-dataZoom will only change the range of yAxis.
|
// So we should filter x-axis after reset x-axis immediately,
|
// and then reset y-axis and filter y-axis.
|
dataZoomModel.eachTargetAxis(filterSingleAxis);
|
});
|
|
ecModel.eachComponent('dataZoom', function (dataZoomModel) {
|
// Fullfill all of the range props so that user
|
// is able to get them from chart.getOption().
|
var axisProxy = dataZoomModel.findRepresentativeAxisProxy();
|
var percentRange = axisProxy.getDataPercentWindow();
|
var valueRange = axisProxy.getDataValueWindow();
|
|
dataZoomModel.setRawRange({
|
start: percentRange[0],
|
end: percentRange[1],
|
startValue: valueRange[0],
|
endValue: valueRange[1]
|
}, true);
|
});
|
});
|
|
function resetSingleAxis(dimNames, axisIndex, dataZoomModel) {
|
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel);
|
}
|
|
function filterSingleAxis(dimNames, axisIndex, dataZoomModel) {
|
dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel);
|
}
|
|
|
|
|
/***/ },
|
/* 367 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom action
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var helper = __webpack_require__(358);
|
var echarts = __webpack_require__(1);
|
|
|
echarts.registerAction('dataZoom', function (payload, ecModel) {
|
|
var linkedNodesFinder = helper.createLinkedNodesFinder(
|
zrUtil.bind(ecModel.eachComponent, ecModel, 'dataZoom'),
|
helper.eachAxisDim,
|
function (model, dimNames) {
|
return model.get(dimNames.axisIndex);
|
}
|
);
|
|
var effectedModels = [];
|
|
ecModel.eachComponent(
|
{mainType: 'dataZoom', query: payload},
|
function (model, index) {
|
effectedModels.push.apply(
|
effectedModels, linkedNodesFinder(model).nodes
|
);
|
}
|
);
|
|
zrUtil.each(effectedModels, function (dataZoomModel, index) {
|
dataZoomModel.setRawRange({
|
start: payload.start,
|
end: payload.end,
|
startValue: payload.startValue,
|
endValue: payload.endValue
|
});
|
});
|
|
});
|
|
|
|
/***/ },
|
/* 368 */,
|
/* 369 */,
|
/* 370 */,
|
/* 371 */,
|
/* 372 */,
|
/* 373 */,
|
/* 374 */,
|
/* 375 */,
|
/* 376 */,
|
/* 377 */,
|
/* 378 */,
|
/* 379 */,
|
/* 380 */,
|
/* 381 */,
|
/* 382 */,
|
/* 383 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// HINT Markpoint can't be used too much
|
|
|
__webpack_require__(384);
|
__webpack_require__(386);
|
|
__webpack_require__(1).registerPreprocessor(function (opt) {
|
// Make sure markPoint component is enabled
|
opt.markPoint = opt.markPoint || {};
|
});
|
|
|
/***/ },
|
/* 384 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(385).extend({
|
|
type: 'markPoint',
|
|
defaultOption: {
|
zlevel: 0,
|
z: 5,
|
symbol: 'pin',
|
symbolSize: 50,
|
//symbolRotate: 0,
|
//symbolOffset: [0, 0]
|
tooltip: {
|
trigger: 'item'
|
},
|
label: {
|
normal: {
|
show: true,
|
position: 'inside'
|
},
|
emphasis: {
|
show: true
|
}
|
},
|
itemStyle: {
|
normal: {
|
borderWidth: 2
|
}
|
}
|
}
|
});
|
|
|
/***/ },
|
/* 385 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var modelUtil = __webpack_require__(5);
|
var zrUtil = __webpack_require__(4);
|
var env = __webpack_require__(2);
|
|
var formatUtil = __webpack_require__(6);
|
var addCommas = formatUtil.addCommas;
|
var encodeHTML = formatUtil.encodeHTML;
|
|
function fillLabel(opt) {
|
modelUtil.defaultEmphasis(
|
opt.label,
|
modelUtil.LABEL_OPTIONS
|
);
|
}
|
var MarkerModel = __webpack_require__(1).extendComponentModel({
|
|
type: 'marker',
|
|
dependencies: ['series', 'grid', 'polar', 'geo'],
|
/**
|
* @overrite
|
*/
|
init: function (option, parentModel, ecModel, extraOpt) {
|
|
if (true) {
|
if (this.type === 'marker') {
|
throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.');
|
}
|
}
|
this.mergeDefaultAndTheme(option, ecModel);
|
this.mergeOption(option, ecModel, extraOpt.createdBySelf, true);
|
},
|
|
/**
|
* @return {boolean}
|
*/
|
isAnimationEnabled: function () {
|
if (env.node) {
|
return false;
|
}
|
|
var hostSeries = this.__hostSeries;
|
return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled();
|
},
|
|
mergeOption: function (newOpt, ecModel, createdBySelf, isInit) {
|
var MarkerModel = this.constructor;
|
var modelPropName = this.mainType + 'Model';
|
if (!createdBySelf) {
|
ecModel.eachSeries(function (seriesModel) {
|
|
var markerOpt = seriesModel.get(this.mainType);
|
|
var markerModel = seriesModel[modelPropName];
|
if (!markerOpt || !markerOpt.data) {
|
seriesModel[modelPropName] = null;
|
return;
|
}
|
if (!markerModel) {
|
if (isInit) {
|
// Default label emphasis `position` and `show`
|
fillLabel(markerOpt);
|
}
|
zrUtil.each(markerOpt.data, function (item) {
|
// FIXME Overwrite fillLabel method ?
|
if (item instanceof Array) {
|
fillLabel(item[0]);
|
fillLabel(item[1]);
|
}
|
else {
|
fillLabel(item);
|
}
|
});
|
|
markerModel = new MarkerModel(
|
markerOpt, this, ecModel
|
);
|
|
zrUtil.extend(markerModel, {
|
mainType: this.mainType,
|
// Use the same series index and name
|
seriesIndex: seriesModel.seriesIndex,
|
name: seriesModel.name,
|
createdBySelf: true
|
});
|
|
markerModel.__hostSeries = seriesModel;
|
}
|
else {
|
markerModel.mergeOption(markerOpt, ecModel, true);
|
}
|
seriesModel[modelPropName] = markerModel;
|
}, this);
|
}
|
},
|
|
formatTooltip: function (dataIndex) {
|
var data = this.getData();
|
var value = this.getRawValue(dataIndex);
|
var formattedValue = zrUtil.isArray(value)
|
? zrUtil.map(value, addCommas).join(', ') : addCommas(value);
|
var name = data.getName(dataIndex);
|
var html = encodeHTML(this.name);
|
if (value != null || name) {
|
html += '<br />';
|
}
|
if (name) {
|
html += encodeHTML(name);
|
if (value != null) {
|
html += ' : ';
|
}
|
}
|
if (value != null) {
|
html += encodeHTML(formattedValue);
|
}
|
return html;
|
},
|
|
getData: function () {
|
return this._data;
|
},
|
|
setData: function (data) {
|
this._data = data;
|
}
|
});
|
|
zrUtil.mixin(MarkerModel, modelUtil.dataFormatMixin);
|
|
module.exports = MarkerModel;
|
|
|
/***/ },
|
/* 386 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var SymbolDraw = __webpack_require__(116);
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
|
var List = __webpack_require__(98);
|
|
var markerHelper = __webpack_require__(387);
|
|
function updateMarkerLayout(mpData, seriesModel, api) {
|
var coordSys = seriesModel.coordinateSystem;
|
mpData.each(function (idx) {
|
var itemModel = mpData.getItemModel(idx);
|
var point;
|
var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth());
|
var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight());
|
if (!isNaN(xPx) && !isNaN(yPx)) {
|
point = [xPx, yPx];
|
}
|
// Chart like bar may have there own marker positioning logic
|
else if (seriesModel.getMarkerPosition) {
|
// Use the getMarkerPoisition
|
point = seriesModel.getMarkerPosition(
|
mpData.getValues(mpData.dimensions, idx)
|
);
|
}
|
else if (coordSys) {
|
var x = mpData.get(coordSys.dimensions[0], idx);
|
var y = mpData.get(coordSys.dimensions[1], idx);
|
point = coordSys.dataToPoint([x, y]);
|
|
}
|
|
// Use x, y if has any
|
if (!isNaN(xPx)) {
|
point[0] = xPx;
|
}
|
if (!isNaN(yPx)) {
|
point[1] = yPx;
|
}
|
|
mpData.setItemLayout(idx, point);
|
});
|
}
|
|
__webpack_require__(388).extend({
|
|
type: 'markPoint',
|
|
updateLayout: function (markPointModel, ecModel, api) {
|
ecModel.eachSeries(function (seriesModel) {
|
var mpModel = seriesModel.markPointModel;
|
if (mpModel) {
|
updateMarkerLayout(mpModel.getData(), seriesModel, api);
|
this.markerGroupMap[seriesModel.name].updateLayout(mpModel);
|
}
|
}, this);
|
},
|
|
renderSeries: function (seriesModel, mpModel, ecModel, api) {
|
var coordSys = seriesModel.coordinateSystem;
|
var seriesName = seriesModel.name;
|
var seriesData = seriesModel.getData();
|
|
var symbolDrawMap = this.markerGroupMap;
|
var symbolDraw = symbolDrawMap[seriesName];
|
if (!symbolDraw) {
|
symbolDraw = symbolDrawMap[seriesName] = new SymbolDraw();
|
}
|
|
var mpData = createList(coordSys, seriesModel, mpModel);
|
|
// FIXME
|
mpModel.setData(mpData);
|
|
updateMarkerLayout(mpModel.getData(), seriesModel, api);
|
|
mpData.each(function (idx) {
|
var itemModel = mpData.getItemModel(idx);
|
var symbolSize = itemModel.getShallow('symbolSize');
|
if (typeof symbolSize === 'function') {
|
// FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据?
|
symbolSize = symbolSize(
|
mpModel.getRawValue(idx), mpModel.getDataParams(idx)
|
);
|
}
|
mpData.setItemVisual(idx, {
|
symbolSize: symbolSize,
|
color: itemModel.get('itemStyle.normal.color')
|
|| seriesData.getVisual('color'),
|
symbol: itemModel.getShallow('symbol')
|
});
|
});
|
|
// TODO Text are wrong
|
symbolDraw.updateData(mpData);
|
this.group.add(symbolDraw.group);
|
|
// Set host model for tooltip
|
// FIXME
|
mpData.eachItemGraphicEl(function (el) {
|
el.traverse(function (child) {
|
child.dataModel = mpModel;
|
});
|
});
|
|
symbolDraw.__keep = true;
|
|
symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent');
|
}
|
});
|
|
/**
|
* @inner
|
* @param {module:echarts/coord/*} [coordSys]
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Model} mpModel
|
*/
|
function createList(coordSys, seriesModel, mpModel) {
|
var coordDimsInfos;
|
if (coordSys) {
|
coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
|
var info = seriesModel.getData().getDimensionInfo(
|
seriesModel.coordDimToDataDim(coordDim)[0]
|
) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
|
info.name = coordDim;
|
return info;
|
});
|
}
|
else {
|
coordDimsInfos =[{
|
name: 'value',
|
type: 'float'
|
}];
|
}
|
|
var mpData = new List(coordDimsInfos, mpModel);
|
var dataOpt = zrUtil.map(mpModel.get('data'), zrUtil.curry(
|
markerHelper.dataTransform, seriesModel
|
));
|
if (coordSys) {
|
dataOpt = zrUtil.filter(
|
dataOpt, zrUtil.curry(markerHelper.dataFilter, coordSys)
|
);
|
}
|
|
mpData.initData(dataOpt, null,
|
coordSys ? markerHelper.dimValueGetter : function (item) {
|
return item.value;
|
}
|
);
|
return mpData;
|
}
|
|
|
|
/***/ },
|
/* 387 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var numberUtil = __webpack_require__(7);
|
var indexOf = zrUtil.indexOf;
|
|
function hasXOrY(item) {
|
return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y)));
|
}
|
|
function hasXAndY(item) {
|
return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y));
|
}
|
|
function getPrecision(data, valueAxisDim, dataIndex) {
|
var precision = -1;
|
do {
|
precision = Math.max(
|
numberUtil.getPrecision(data.get(
|
valueAxisDim, dataIndex
|
)),
|
precision
|
);
|
data = data.stackedOn;
|
} while (data);
|
|
return precision;
|
}
|
|
function markerTypeCalculatorWithExtent(
|
mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex
|
) {
|
var coordArr = [];
|
var value = numCalculate(data, targetDataDim, mlType);
|
|
var dataIndex = data.indicesOfNearest(targetDataDim, value, true)[0];
|
coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex, true);
|
coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex, true);
|
|
var precision = getPrecision(data, targetDataDim, dataIndex);
|
if (precision >= 0) {
|
coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision);
|
}
|
|
return coordArr;
|
}
|
|
var curry = zrUtil.curry;
|
// TODO Specified percent
|
var markerTypeCalculator = {
|
/**
|
* @method
|
* @param {module:echarts/data/List} data
|
* @param {string} baseAxisDim
|
* @param {string} valueAxisDim
|
*/
|
min: curry(markerTypeCalculatorWithExtent, 'min'),
|
/**
|
* @method
|
* @param {module:echarts/data/List} data
|
* @param {string} baseAxisDim
|
* @param {string} valueAxisDim
|
*/
|
max: curry(markerTypeCalculatorWithExtent, 'max'),
|
|
/**
|
* @method
|
* @param {module:echarts/data/List} data
|
* @param {string} baseAxisDim
|
* @param {string} valueAxisDim
|
*/
|
average: curry(markerTypeCalculatorWithExtent, 'average')
|
};
|
|
/**
|
* Transform markPoint data item to format used in List by do the following
|
* 1. Calculate statistic like `max`, `min`, `average`
|
* 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/coord/*} [coordSys]
|
* @param {Object} item
|
* @return {Object}
|
*/
|
var dataTransform = function (seriesModel, item) {
|
var data = seriesModel.getData();
|
var coordSys = seriesModel.coordinateSystem;
|
|
// 1. If not specify the position with pixel directly
|
// 2. If `coord` is not a data array. Which uses `xAxis`,
|
// `yAxis` to specify the coord on each dimension
|
|
// parseFloat first because item.x and item.y can be percent string like '20%'
|
if (item && !hasXAndY(item) && !zrUtil.isArray(item.coord) && coordSys) {
|
var dims = coordSys.dimensions;
|
var axisInfo = getAxisInfo(item, data, coordSys, seriesModel);
|
|
// Clone the option
|
// Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value
|
item = zrUtil.clone(item);
|
|
if (item.type
|
&& markerTypeCalculator[item.type]
|
&& axisInfo.baseAxis && axisInfo.valueAxis
|
) {
|
var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim);
|
var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim);
|
|
item.coord = markerTypeCalculator[item.type](
|
data, axisInfo.baseDataDim, axisInfo.valueDataDim,
|
otherCoordIndex, targetCoordIndex
|
);
|
// Force to use the value of calculated value.
|
item.value = item.coord[targetCoordIndex];
|
}
|
else {
|
// FIXME Only has one of xAxis and yAxis.
|
var coord = [
|
item.xAxis != null ? item.xAxis : item.radiusAxis,
|
item.yAxis != null ? item.yAxis : item.angleAxis
|
];
|
// Each coord support max, min, average
|
for (var i = 0; i < 2; i++) {
|
if (markerTypeCalculator[coord[i]]) {
|
var dataDim = seriesModel.coordDimToDataDim(dims[i])[0];
|
coord[i] = numCalculate(data, dataDim, coord[i]);
|
}
|
}
|
item.coord = coord;
|
}
|
}
|
return item;
|
};
|
|
var getAxisInfo = function (item, data, coordSys, seriesModel) {
|
var ret = {};
|
|
if (item.valueIndex != null || item.valueDim != null) {
|
ret.valueDataDim = item.valueIndex != null
|
? data.getDimension(item.valueIndex) : item.valueDim;
|
ret.valueAxis = coordSys.getAxis(seriesModel.dataDimToCoordDim(ret.valueDataDim));
|
ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis);
|
ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0];
|
}
|
else {
|
ret.baseAxis = seriesModel.getBaseAxis();
|
ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis);
|
ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0];
|
ret.valueDataDim = seriesModel.coordDimToDataDim(ret.valueAxis.dim)[0];
|
}
|
|
return ret;
|
};
|
|
/**
|
* Filter data which is out of coordinateSystem range
|
* [dataFilter description]
|
* @param {module:echarts/coord/*} [coordSys]
|
* @param {Object} item
|
* @return {boolean}
|
*/
|
var dataFilter = function (coordSys, item) {
|
// Alwalys return true if there is no coordSys
|
return (coordSys && coordSys.containData && item.coord && !hasXOrY(item))
|
? coordSys.containData(item.coord) : true;
|
};
|
|
var dimValueGetter = function (item, dimName, dataIndex, dimIndex) {
|
// x, y, radius, angle
|
if (dimIndex < 2) {
|
return item.coord && item.coord[dimIndex];
|
}
|
return item.value;
|
};
|
|
var numCalculate = function (data, valueDataDim, type) {
|
if (type === 'average') {
|
var sum = 0;
|
var count = 0;
|
data.each(valueDataDim, function (val, idx) {
|
if (!isNaN(val)) {
|
sum += val;
|
count++;
|
}
|
}, true);
|
return sum / count;
|
}
|
else {
|
return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0];
|
}
|
};
|
|
module.exports = {
|
dataTransform: dataTransform,
|
dataFilter: dataFilter,
|
dimValueGetter: dimValueGetter,
|
getAxisInfo: getAxisInfo,
|
numCalculate: numCalculate
|
};
|
|
|
/***/ },
|
/* 388 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(1).extendComponentView({
|
|
type: 'marker',
|
|
init: function () {
|
/**
|
* Markline grouped by series
|
* @private
|
* @type {Object}
|
*/
|
this.markerGroupMap = {};
|
},
|
|
render: function (markerModel, ecModel, api) {
|
var markerGroupMap = this.markerGroupMap;
|
for (var name in markerGroupMap) {
|
if (markerGroupMap.hasOwnProperty(name)) {
|
markerGroupMap[name].__keep = false;
|
}
|
}
|
|
var markerModelKey = this.type + 'Model';
|
ecModel.eachSeries(function (seriesModel) {
|
var markerModel = seriesModel[markerModelKey];
|
markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api);
|
}, this);
|
|
for (var name in markerGroupMap) {
|
if (markerGroupMap.hasOwnProperty(name) && !markerGroupMap[name].__keep) {
|
this.group.remove(markerGroupMap[name].group);
|
}
|
}
|
},
|
|
renderSeries: function () {}
|
});
|
|
|
/***/ },
|
/* 389 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(390);
|
__webpack_require__(391);
|
|
__webpack_require__(1).registerPreprocessor(function (opt) {
|
// Make sure markLine component is enabled
|
opt.markLine = opt.markLine || {};
|
});
|
|
|
/***/ },
|
/* 390 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(385).extend({
|
|
type: 'markLine',
|
|
defaultOption: {
|
zlevel: 0,
|
z: 5,
|
|
symbol: ['circle', 'arrow'],
|
symbolSize: [8, 16],
|
|
//symbolRotate: 0,
|
|
precision: 2,
|
tooltip: {
|
trigger: 'item'
|
},
|
label: {
|
normal: {
|
show: true,
|
position: 'end'
|
},
|
emphasis: {
|
show: true
|
}
|
},
|
lineStyle: {
|
normal: {
|
type: 'dashed'
|
},
|
emphasis: {
|
width: 3
|
}
|
},
|
animationEasing: 'linear'
|
}
|
});
|
|
|
/***/ },
|
/* 391 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var List = __webpack_require__(98);
|
var numberUtil = __webpack_require__(7);
|
|
var markerHelper = __webpack_require__(387);
|
|
var LineDraw = __webpack_require__(209);
|
|
var markLineTransform = function (seriesModel, coordSys, mlModel, item) {
|
var data = seriesModel.getData();
|
// Special type markLine like 'min', 'max', 'average'
|
var mlType = item.type;
|
|
if (!zrUtil.isArray(item)
|
&& (
|
mlType === 'min' || mlType === 'max' || mlType === 'average'
|
// In case
|
// data: [{
|
// yAxis: 10
|
// }]
|
|| (item.xAxis != null || item.yAxis != null)
|
)
|
) {
|
var valueAxis;
|
var valueDataDim;
|
var value;
|
|
if (item.yAxis != null || item.xAxis != null) {
|
valueDataDim = item.yAxis != null ? 'y' : 'x';
|
valueAxis = coordSys.getAxis(valueDataDim);
|
|
value = zrUtil.retrieve(item.yAxis, item.xAxis);
|
}
|
else {
|
var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel);
|
valueDataDim = axisInfo.valueDataDim;
|
valueAxis = axisInfo.valueAxis;
|
value = markerHelper.numCalculate(data, valueDataDim, mlType);
|
}
|
var valueIndex = valueDataDim === 'x' ? 0 : 1;
|
var baseIndex = 1 - valueIndex;
|
|
var mlFrom = zrUtil.clone(item);
|
var mlTo = {};
|
|
mlFrom.type = null;
|
|
mlFrom.coord = [];
|
mlTo.coord = [];
|
mlFrom.coord[baseIndex] = -Infinity;
|
mlTo.coord[baseIndex] = Infinity;
|
|
var precision = mlModel.get('precision');
|
if (precision >= 0 && typeof value === 'number') {
|
value = +value.toFixed(precision);
|
}
|
|
mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value;
|
|
item = [mlFrom, mlTo, { // Extra option for tooltip and label
|
type: mlType,
|
valueIndex: item.valueIndex,
|
// Force to use the value of calculated value.
|
value: value
|
}];
|
}
|
|
item = [
|
markerHelper.dataTransform(seriesModel, item[0]),
|
markerHelper.dataTransform(seriesModel, item[1]),
|
zrUtil.extend({}, item[2])
|
];
|
|
// Avoid line data type is extended by from(to) data type
|
item[2].type = item[2].type || '';
|
|
// Merge from option and to option into line option
|
zrUtil.merge(item[2], item[0]);
|
zrUtil.merge(item[2], item[1]);
|
|
return item;
|
};
|
|
function isInifinity(val) {
|
return !isNaN(val) && !isFinite(val);
|
}
|
|
// If a markLine has one dim
|
function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
|
var otherDimIndex = 1 - dimIndex;
|
var dimName = coordSys.dimensions[dimIndex];
|
return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex])
|
&& fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]);
|
}
|
|
function markLineFilter(coordSys, item) {
|
if (coordSys.type === 'cartesian2d') {
|
var fromCoord = item[0].coord;
|
var toCoord = item[1].coord;
|
// In case
|
// {
|
// markLine: {
|
// data: [{ yAxis: 2 }]
|
// }
|
// }
|
if (
|
fromCoord && toCoord &&
|
(ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
|
|| ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
|
) {
|
return true;
|
}
|
}
|
return markerHelper.dataFilter(coordSys, item[0])
|
&& markerHelper.dataFilter(coordSys, item[1]);
|
}
|
|
function updateSingleMarkerEndLayout(
|
data, idx, isFrom, seriesModel, api
|
) {
|
var coordSys = seriesModel.coordinateSystem;
|
var itemModel = data.getItemModel(idx);
|
|
var point;
|
var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth());
|
var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight());
|
if (!isNaN(xPx) && !isNaN(yPx)) {
|
point = [xPx, yPx];
|
}
|
else {
|
// Chart like bar may have there own marker positioning logic
|
if (seriesModel.getMarkerPosition) {
|
// Use the getMarkerPoisition
|
point = seriesModel.getMarkerPosition(
|
data.getValues(data.dimensions, idx)
|
);
|
}
|
else {
|
var dims = coordSys.dimensions;
|
var x = data.get(dims[0], idx);
|
var y = data.get(dims[1], idx);
|
point = coordSys.dataToPoint([x, y]);
|
}
|
// Expand line to the edge of grid if value on one axis is Inifnity
|
// In case
|
// markLine: {
|
// data: [{
|
// yAxis: 2
|
// // or
|
// type: 'average'
|
// }]
|
// }
|
if (coordSys.type === 'cartesian2d') {
|
var xAxis = coordSys.getAxis('x');
|
var yAxis = coordSys.getAxis('y');
|
var dims = coordSys.dimensions;
|
if (isInifinity(data.get(dims[0], idx))) {
|
point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]);
|
}
|
else if (isInifinity(data.get(dims[1], idx))) {
|
point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]);
|
}
|
}
|
|
// Use x, y if has any
|
if (!isNaN(xPx)) {
|
point[0] = xPx;
|
}
|
if (!isNaN(yPx)) {
|
point[1] = yPx;
|
}
|
}
|
|
data.setItemLayout(idx, point);
|
}
|
|
__webpack_require__(388).extend({
|
|
type: 'markLine',
|
|
updateLayout: function (markLineModel, ecModel, api) {
|
ecModel.eachSeries(function (seriesModel) {
|
var mlModel = seriesModel.markLineModel;
|
if (mlModel) {
|
var mlData = mlModel.getData();
|
var fromData = mlModel.__from;
|
var toData = mlModel.__to;
|
// Update visual and layout of from symbol and to symbol
|
fromData.each(function (idx) {
|
updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api);
|
updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api);
|
});
|
// Update layout of line
|
mlData.each(function (idx) {
|
mlData.setItemLayout(idx, [
|
fromData.getItemLayout(idx),
|
toData.getItemLayout(idx)
|
]);
|
});
|
|
this.markerGroupMap[seriesModel.name].updateLayout();
|
|
}
|
}, this);
|
},
|
|
renderSeries: function (seriesModel, mlModel, ecModel, api) {
|
var coordSys = seriesModel.coordinateSystem;
|
var seriesName = seriesModel.name;
|
var seriesData = seriesModel.getData();
|
|
var lineDrawMap = this.markerGroupMap;
|
var lineDraw = lineDrawMap[seriesName];
|
if (!lineDraw) {
|
lineDraw = lineDrawMap[seriesName] = new LineDraw();
|
}
|
this.group.add(lineDraw.group);
|
|
var mlData = createList(coordSys, seriesModel, mlModel);
|
|
var fromData = mlData.from;
|
var toData = mlData.to;
|
var lineData = mlData.line;
|
|
mlModel.__from = fromData;
|
mlModel.__to = toData;
|
// Line data for tooltip and formatter
|
mlModel.setData(lineData);
|
|
var symbolType = mlModel.get('symbol');
|
var symbolSize = mlModel.get('symbolSize');
|
if (!zrUtil.isArray(symbolType)) {
|
symbolType = [symbolType, symbolType];
|
}
|
if (typeof symbolSize === 'number') {
|
symbolSize = [symbolSize, symbolSize];
|
}
|
|
// Update visual and layout of from symbol and to symbol
|
mlData.from.each(function (idx) {
|
updateDataVisualAndLayout(fromData, idx, true);
|
updateDataVisualAndLayout(toData, idx, false);
|
});
|
|
// Update visual and layout of line
|
lineData.each(function (idx) {
|
var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color');
|
lineData.setItemVisual(idx, {
|
color: lineColor || fromData.getItemVisual(idx, 'color')
|
});
|
lineData.setItemLayout(idx, [
|
fromData.getItemLayout(idx),
|
toData.getItemLayout(idx)
|
]);
|
|
lineData.setItemVisual(idx, {
|
'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'),
|
'fromSymbol': fromData.getItemVisual(idx, 'symbol'),
|
'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'),
|
'toSymbol': toData.getItemVisual(idx, 'symbol')
|
});
|
});
|
|
lineDraw.updateData(lineData);
|
|
// Set host model for tooltip
|
// FIXME
|
mlData.line.eachItemGraphicEl(function (el, idx) {
|
el.traverse(function (child) {
|
child.dataModel = mlModel;
|
});
|
});
|
|
function updateDataVisualAndLayout(data, idx, isFrom) {
|
var itemModel = data.getItemModel(idx);
|
|
updateSingleMarkerEndLayout(
|
data, idx, isFrom, seriesModel, api
|
);
|
|
data.setItemVisual(idx, {
|
symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1],
|
symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1],
|
color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color')
|
});
|
}
|
|
lineDraw.__keep = true;
|
|
lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent');
|
}
|
});
|
|
/**
|
* @inner
|
* @param {module:echarts/coord/*} coordSys
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Model} mpModel
|
*/
|
function createList(coordSys, seriesModel, mlModel) {
|
|
var coordDimsInfos;
|
if (coordSys) {
|
coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
|
var info = seriesModel.getData().getDimensionInfo(
|
seriesModel.coordDimToDataDim(coordDim)[0]
|
) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
|
info.name = coordDim;
|
return info;
|
});
|
}
|
else {
|
coordDimsInfos =[{
|
name: 'value',
|
type: 'float'
|
}];
|
}
|
|
var fromData = new List(coordDimsInfos, mlModel);
|
var toData = new List(coordDimsInfos, mlModel);
|
// No dimensions
|
var lineData = new List([], mlModel);
|
|
var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry(
|
markLineTransform, seriesModel, coordSys, mlModel
|
));
|
if (coordSys) {
|
optData = zrUtil.filter(
|
optData, zrUtil.curry(markLineFilter, coordSys)
|
);
|
}
|
var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) {
|
return item.value;
|
};
|
fromData.initData(
|
zrUtil.map(optData, function (item) { return item[0]; }),
|
null, dimValueGetter
|
);
|
toData.initData(
|
zrUtil.map(optData, function (item) { return item[1]; }),
|
null, dimValueGetter
|
);
|
lineData.initData(
|
zrUtil.map(optData, function (item) { return item[2]; })
|
);
|
lineData.hasItemOption = true;
|
return {
|
from: fromData,
|
to: toData,
|
line: lineData
|
};
|
}
|
|
|
/***/ },
|
/* 392 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(393);
|
__webpack_require__(394);
|
|
__webpack_require__(1).registerPreprocessor(function (opt) {
|
// Make sure markArea component is enabled
|
opt.markArea = opt.markArea || {};
|
});
|
|
|
/***/ },
|
/* 393 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(385).extend({
|
|
type: 'markArea',
|
|
defaultOption: {
|
zlevel: 0,
|
// PENDING
|
z: 1,
|
tooltip: {
|
trigger: 'item'
|
},
|
// markArea should fixed on the coordinate system
|
animation: false,
|
label: {
|
normal: {
|
show: true,
|
position: 'top'
|
},
|
emphasis: {
|
show: true,
|
position: 'top'
|
}
|
},
|
itemStyle: {
|
normal: {
|
// color and borderColor default to use color from series
|
// color: 'auto'
|
// borderColor: 'auto'
|
borderWidth: 0
|
}
|
}
|
}
|
});
|
|
|
/***/ },
|
/* 394 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// TODO Better on polar
|
|
|
var zrUtil = __webpack_require__(4);
|
var List = __webpack_require__(98);
|
var numberUtil = __webpack_require__(7);
|
var graphic = __webpack_require__(44);
|
var colorUtil = __webpack_require__(39);
|
|
var markerHelper = __webpack_require__(387);
|
|
var markAreaTransform = function (seriesModel, coordSys, maModel, item) {
|
var lt = markerHelper.dataTransform(seriesModel, item[0]);
|
var rb = markerHelper.dataTransform(seriesModel, item[1]);
|
var retrieve = zrUtil.retrieve;
|
|
// FIXME make sure lt is less than rb
|
var ltCoord = lt.coord;
|
var rbCoord = rb.coord;
|
ltCoord[0] = retrieve(ltCoord[0], -Infinity);
|
ltCoord[1] = retrieve(ltCoord[1], -Infinity);
|
|
rbCoord[0] = retrieve(rbCoord[0], Infinity);
|
rbCoord[1] = retrieve(rbCoord[1], Infinity);
|
|
// Merge option into one
|
var result = zrUtil.mergeAll([{}, lt, rb]);
|
|
result.coord = [
|
lt.coord, rb.coord
|
];
|
result.x0 = lt.x;
|
result.y0 = lt.y;
|
result.x1 = rb.x;
|
result.y1 = rb.y;
|
return result;
|
};
|
|
function isInifinity(val) {
|
return !isNaN(val) && !isFinite(val);
|
}
|
|
// If a markArea has one dim
|
function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) {
|
var otherDimIndex = 1 - dimIndex;
|
return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]);
|
}
|
|
function markAreaFilter(coordSys, item) {
|
var fromCoord = item.coord[0];
|
var toCoord = item.coord[1];
|
if (coordSys.type === 'cartesian2d') {
|
// In case
|
// {
|
// markArea: {
|
// data: [{ yAxis: 2 }]
|
// }
|
// }
|
if (
|
fromCoord && toCoord &&
|
(ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys)
|
|| ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))
|
) {
|
return true;
|
}
|
}
|
return markerHelper.dataFilter(coordSys, {
|
coord: fromCoord,
|
x: item.x0,
|
y: item.y0
|
})
|
|| markerHelper.dataFilter(coordSys, {
|
coord: toCoord,
|
x: item.x1,
|
y: item.y1
|
});
|
}
|
|
// dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0']
|
function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) {
|
var coordSys = seriesModel.coordinateSystem;
|
var itemModel = data.getItemModel(idx);
|
|
var point;
|
var xPx = numberUtil.parsePercent(itemModel.get(dims[0]), api.getWidth());
|
var yPx = numberUtil.parsePercent(itemModel.get(dims[1]), api.getHeight());
|
if (!isNaN(xPx) && !isNaN(yPx)) {
|
point = [xPx, yPx];
|
}
|
else {
|
// Chart like bar may have there own marker positioning logic
|
if (seriesModel.getMarkerPosition) {
|
// Use the getMarkerPoisition
|
point = seriesModel.getMarkerPosition(
|
data.getValues(dims, idx)
|
);
|
}
|
else {
|
var x = data.get(dims[0], idx);
|
var y = data.get(dims[1], idx);
|
point = coordSys.dataToPoint([x, y], true);
|
}
|
if (coordSys.type === 'cartesian2d') {
|
var xAxis = coordSys.getAxis('x');
|
var yAxis = coordSys.getAxis('y');
|
var x = data.get(dims[0], idx);
|
var y = data.get(dims[1], idx);
|
if (isInifinity(x)) {
|
point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]);
|
}
|
else if (isInifinity(y)) {
|
point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]);
|
}
|
}
|
|
// Use x, y if has any
|
if (!isNaN(xPx)) {
|
point[0] = xPx;
|
}
|
if (!isNaN(yPx)) {
|
point[1] = yPx;
|
}
|
}
|
|
return point;
|
}
|
|
var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']];
|
|
__webpack_require__(388).extend({
|
|
type: 'markArea',
|
|
updateLayout: function (markAreaModel, ecModel, api) {
|
ecModel.eachSeries(function (seriesModel) {
|
var maModel = seriesModel.markAreaModel;
|
if (maModel) {
|
var areaData = maModel.getData();
|
areaData.each(function (idx) {
|
var points = zrUtil.map(dimPermutations, function (dim) {
|
return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
|
});
|
// Layout
|
areaData.setItemLayout(idx, points);
|
var el = areaData.getItemGraphicEl(idx);
|
el.setShape('points', points);
|
});
|
}
|
}, this);
|
},
|
|
renderSeries: function (seriesModel, maModel, ecModel, api) {
|
var coordSys = seriesModel.coordinateSystem;
|
var seriesName = seriesModel.name;
|
var seriesData = seriesModel.getData();
|
|
var areaGroupMap = this.markerGroupMap;
|
var polygonGroup = areaGroupMap[seriesName];
|
if (!polygonGroup) {
|
polygonGroup = areaGroupMap[seriesName] = {
|
group: new graphic.Group()
|
};
|
}
|
this.group.add(polygonGroup.group);
|
polygonGroup.__keep = true;
|
|
var areaData = createList(coordSys, seriesModel, maModel);
|
|
// Line data for tooltip and formatter
|
maModel.setData(areaData);
|
|
// Update visual and layout of line
|
areaData.each(function (idx) {
|
// Layout
|
areaData.setItemLayout(idx, zrUtil.map(dimPermutations, function (dim) {
|
return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api);
|
}));
|
|
// Visual
|
areaData.setItemVisual(idx, {
|
color: seriesData.getVisual('color')
|
});
|
});
|
|
|
areaData.diff(polygonGroup.__data)
|
.add(function (idx) {
|
var polygon = new graphic.Polygon({
|
shape: {
|
points: areaData.getItemLayout(idx)
|
}
|
});
|
areaData.setItemGraphicEl(idx, polygon);
|
polygonGroup.group.add(polygon);
|
})
|
.update(function (newIdx, oldIdx) {
|
var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx);
|
graphic.updateProps(polygon, {
|
shape: {
|
points: areaData.getItemLayout(newIdx)
|
}
|
}, maModel, newIdx);
|
polygonGroup.group.add(polygon);
|
areaData.setItemGraphicEl(newIdx, polygon);
|
})
|
.remove(function (idx) {
|
var polygon = polygonGroup.__data.getItemGraphicEl(idx);
|
polygonGroup.group.remove(polygon);
|
})
|
.execute();
|
|
areaData.eachItemGraphicEl(function (polygon, idx) {
|
var itemModel = areaData.getItemModel(idx);
|
var labelModel = itemModel.getModel('label.normal');
|
var labelHoverModel = itemModel.getModel('label.emphasis');
|
var color = areaData.getItemVisual(idx, 'color');
|
polygon.useStyle(
|
zrUtil.defaults(
|
itemModel.getModel('itemStyle.normal').getItemStyle(),
|
{
|
fill: colorUtil.modifyAlpha(color, 0.4),
|
stroke: color
|
}
|
)
|
);
|
|
polygon.hoverStyle = itemModel.getModel('itemStyle.normal').getItemStyle();
|
|
var defaultValue = areaData.getName(idx) || '';
|
var textColor = color || polygon.style.fill;
|
|
if (labelModel.getShallow('show')) {
|
graphic.setText(polygon.style, labelModel, textColor);
|
polygon.style.text = zrUtil.retrieve(
|
maModel.getFormattedLabel(idx, 'normal'),
|
defaultValue
|
);
|
}
|
else {
|
polygon.style.text = '';
|
}
|
|
if (labelHoverModel.getShallow('show')) {
|
graphic.setText(polygon.hoverStyle, labelHoverModel, textColor);
|
polygon.hoverStyle.text = zrUtil.retrieve(
|
maModel.getFormattedLabel(idx, 'emphasis'),
|
defaultValue
|
);
|
}
|
else {
|
polygon.hoverStyle.text = '';
|
}
|
|
graphic.setHoverStyle(polygon, {});
|
|
polygon.dataModel = maModel;
|
});
|
|
polygonGroup.__data = areaData;
|
|
polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent');
|
}
|
});
|
|
/**
|
* @inner
|
* @param {module:echarts/coord/*} coordSys
|
* @param {module:echarts/model/Series} seriesModel
|
* @param {module:echarts/model/Model} mpModel
|
*/
|
function createList(coordSys, seriesModel, maModel) {
|
|
var coordDimsInfos;
|
var areaData;
|
var dims = ['x0', 'y0', 'x1', 'y1'];
|
if (coordSys) {
|
coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) {
|
var info = seriesModel.getData().getDimensionInfo(
|
seriesModel.coordDimToDataDim(coordDim)[0]
|
) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys
|
info.name = coordDim;
|
return info;
|
});
|
areaData = new List(zrUtil.map(dims, function (dim, idx) {
|
return {
|
name: dim,
|
type: coordDimsInfos[idx % 2].type
|
};
|
}), maModel);
|
}
|
else {
|
coordDimsInfos =[{
|
name: 'value',
|
type: 'float'
|
}];
|
areaData = new List(coordDimsInfos, maModel);
|
}
|
|
var optData = zrUtil.map(maModel.get('data'), zrUtil.curry(
|
markAreaTransform, seriesModel, coordSys, maModel
|
));
|
if (coordSys) {
|
optData = zrUtil.filter(
|
optData, zrUtil.curry(markAreaFilter, coordSys)
|
);
|
}
|
|
var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) {
|
return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2];
|
} : function (item) {
|
return item.value;
|
};
|
areaData.initData(optData, null, dimValueGetter);
|
areaData.hasItemOption = true;
|
return areaData;
|
}
|
|
|
/***/ },
|
/* 395 */,
|
/* 396 */,
|
/* 397 */,
|
/* 398 */,
|
/* 399 */,
|
/* 400 */,
|
/* 401 */,
|
/* 402 */,
|
/* 403 */,
|
/* 404 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
__webpack_require__(405);
|
__webpack_require__(406);
|
|
__webpack_require__(407);
|
__webpack_require__(408);
|
__webpack_require__(409);
|
__webpack_require__(410);
|
__webpack_require__(415);
|
|
|
/***/ },
|
/* 405 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var featureManager = __webpack_require__(349);
|
var zrUtil = __webpack_require__(4);
|
|
var ToolboxModel = __webpack_require__(1).extendComponentModel({
|
|
type: 'toolbox',
|
|
layoutMode: {
|
type: 'box',
|
ignoreSize: true
|
},
|
|
mergeDefaultAndTheme: function (option) {
|
ToolboxModel.superApply(this, 'mergeDefaultAndTheme', arguments);
|
|
zrUtil.each(this.option.feature, function (featureOpt, featureName) {
|
var Feature = featureManager.get(featureName);
|
Feature && zrUtil.merge(featureOpt, Feature.defaultOption);
|
});
|
},
|
|
defaultOption: {
|
|
show: true,
|
|
z: 6,
|
|
zlevel: 0,
|
|
orient: 'horizontal',
|
|
left: 'right',
|
|
top: 'top',
|
|
// right
|
// bottom
|
|
backgroundColor: 'transparent',
|
|
borderColor: '#ccc',
|
|
borderWidth: 0,
|
|
padding: 5,
|
|
itemSize: 15,
|
|
itemGap: 8,
|
|
showTitle: true,
|
|
iconStyle: {
|
normal: {
|
borderColor: '#666',
|
color: 'none'
|
},
|
emphasis: {
|
borderColor: '#3E98C5'
|
}
|
}
|
// textStyle: {},
|
|
// feature
|
}
|
});
|
|
module.exports = ToolboxModel;
|
|
|
/***/ },
|
/* 406 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/* WEBPACK VAR INJECTION */(function(process) {
|
|
var featureManager = __webpack_require__(349);
|
var zrUtil = __webpack_require__(4);
|
var graphic = __webpack_require__(44);
|
var Model = __webpack_require__(12);
|
var DataDiffer = __webpack_require__(99);
|
var listComponentHelper = __webpack_require__(318);
|
var textContain = __webpack_require__(8);
|
|
module.exports = __webpack_require__(1).extendComponentView({
|
|
type: 'toolbox',
|
|
render: function (toolboxModel, ecModel, api, payload) {
|
var group = this.group;
|
group.removeAll();
|
|
if (!toolboxModel.get('show')) {
|
return;
|
}
|
|
var itemSize = +toolboxModel.get('itemSize');
|
var featureOpts = toolboxModel.get('feature') || {};
|
var features = this._features || (this._features = {});
|
|
var featureNames = [];
|
zrUtil.each(featureOpts, function (opt, name) {
|
featureNames.push(name);
|
});
|
|
(new DataDiffer(this._featureNames || [], featureNames))
|
.add(process)
|
.update(process)
|
.remove(zrUtil.curry(process, null))
|
.execute();
|
|
// Keep for diff.
|
this._featureNames = featureNames;
|
|
function process(newIndex, oldIndex) {
|
var featureName = featureNames[newIndex];
|
var oldName = featureNames[oldIndex];
|
var featureOpt = featureOpts[featureName];
|
var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel);
|
var feature;
|
|
if (featureName && !oldName) { // Create
|
if (isUserFeatureName(featureName)) {
|
feature = {
|
model: featureModel,
|
onclick: featureModel.option.onclick,
|
featureName: featureName
|
};
|
}
|
else {
|
var Feature = featureManager.get(featureName);
|
if (!Feature) {
|
return;
|
}
|
feature = new Feature(featureModel, ecModel, api);
|
}
|
features[featureName] = feature;
|
}
|
else {
|
feature = features[oldName];
|
// If feature does not exsit.
|
if (!feature) {
|
return;
|
}
|
feature.model = featureModel;
|
feature.ecModel = ecModel;
|
feature.api = api;
|
}
|
|
if (!featureName && oldName) {
|
feature.dispose && feature.dispose(ecModel, api);
|
return;
|
}
|
|
if (!featureModel.get('show') || feature.unusable) {
|
feature.remove && feature.remove(ecModel, api);
|
return;
|
}
|
|
createIconPaths(featureModel, feature, featureName);
|
|
featureModel.setIconStatus = function (iconName, status) {
|
var option = this.option;
|
var iconPaths = this.iconPaths;
|
option.iconStatus = option.iconStatus || {};
|
option.iconStatus[iconName] = status;
|
// FIXME
|
iconPaths[iconName] && iconPaths[iconName].trigger(status);
|
};
|
|
if (feature.render) {
|
feature.render(featureModel, ecModel, api, payload);
|
}
|
}
|
|
function createIconPaths(featureModel, feature, featureName) {
|
var iconStyleModel = featureModel.getModel('iconStyle');
|
|
// If one feature has mutiple icon. they are orginaized as
|
// {
|
// icon: {
|
// foo: '',
|
// bar: ''
|
// },
|
// title: {
|
// foo: '',
|
// bar: ''
|
// }
|
// }
|
var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon');
|
var titles = featureModel.get('title') || {};
|
if (typeof icons === 'string') {
|
var icon = icons;
|
var title = titles;
|
icons = {};
|
titles = {};
|
icons[featureName] = icon;
|
titles[featureName] = title;
|
}
|
var iconPaths = featureModel.iconPaths = {};
|
zrUtil.each(icons, function (icon, iconName) {
|
var normalStyle = iconStyleModel.getModel('normal').getItemStyle();
|
var hoverStyle = iconStyleModel.getModel('emphasis').getItemStyle();
|
|
var style = {
|
x: -itemSize / 2,
|
y: -itemSize / 2,
|
width: itemSize,
|
height: itemSize
|
};
|
var path = icon.indexOf('image://') === 0
|
? (
|
style.image = icon.slice(8),
|
new graphic.Image({style: style})
|
)
|
: graphic.makePath(
|
icon.replace('path://', ''),
|
{
|
style: normalStyle,
|
hoverStyle: hoverStyle,
|
rectHover: true
|
},
|
style,
|
'center'
|
);
|
|
graphic.setHoverStyle(path);
|
|
if (toolboxModel.get('showTitle')) {
|
path.__title = titles[iconName];
|
path.on('mouseover', function () {
|
// Should not reuse above hoverStyle, which might be modified.
|
var hoverStyle = iconStyleModel.getModel('emphasis').getItemStyle();
|
path.setStyle({
|
text: titles[iconName],
|
textPosition: hoverStyle.textPosition || 'bottom',
|
textFill: hoverStyle.fill || hoverStyle.stroke || '#000',
|
textAlign: hoverStyle.textAlign || 'center'
|
});
|
})
|
.on('mouseout', function () {
|
path.setStyle({
|
textFill: null
|
});
|
});
|
}
|
path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal');
|
|
group.add(path);
|
path.on('click', zrUtil.bind(
|
feature.onclick, feature, ecModel, api, iconName
|
));
|
|
iconPaths[iconName] = path;
|
});
|
}
|
|
listComponentHelper.layout(group, toolboxModel, api);
|
// Render background after group is layout
|
// FIXME
|
listComponentHelper.addBackground(group, toolboxModel);
|
|
// Adjust icon title positions to avoid them out of screen
|
group.eachChild(function (icon) {
|
var titleText = icon.__title;
|
var hoverStyle = icon.hoverStyle;
|
// May be background element
|
if (hoverStyle && titleText) {
|
var rect = textContain.getBoundingRect(
|
titleText, hoverStyle.font
|
);
|
var offsetX = icon.position[0] + group.position[0];
|
var offsetY = icon.position[1] + group.position[1] + itemSize;
|
|
var needPutOnTop = false;
|
if (offsetY + rect.height > api.getHeight()) {
|
hoverStyle.textPosition = 'top';
|
needPutOnTop = true;
|
}
|
var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8);
|
if (offsetX + rect.width / 2 > api.getWidth()) {
|
hoverStyle.textPosition = ['100%', topOffset];
|
hoverStyle.textAlign = 'right';
|
}
|
else if (offsetX - rect.width / 2 < 0) {
|
hoverStyle.textPosition = [0, topOffset];
|
hoverStyle.textAlign = 'left';
|
}
|
}
|
});
|
},
|
|
updateView: function (toolboxModel, ecModel, api, payload) {
|
zrUtil.each(this._features, function (feature) {
|
feature.updateView && feature.updateView(feature.model, ecModel, api, payload);
|
});
|
},
|
|
updateLayout: function (toolboxModel, ecModel, api, payload) {
|
zrUtil.each(this._features, function (feature) {
|
feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload);
|
});
|
},
|
|
remove: function (ecModel, api) {
|
zrUtil.each(this._features, function (feature) {
|
feature.remove && feature.remove(ecModel, api);
|
});
|
this.group.removeAll();
|
},
|
|
dispose: function (ecModel, api) {
|
zrUtil.each(this._features, function (feature) {
|
feature.dispose && feature.dispose(ecModel, api);
|
});
|
}
|
});
|
|
function isUserFeatureName(featureName) {
|
return featureName.indexOf('my') === 0;
|
}
|
|
|
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(309)))
|
|
/***/ },
|
/* 407 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
var env = __webpack_require__(2);
|
|
function SaveAsImage (model) {
|
this.model = model;
|
}
|
|
SaveAsImage.defaultOption = {
|
show: true,
|
icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0',
|
title: '保存为图片',
|
type: 'png',
|
// Default use option.backgroundColor
|
// backgroundColor: '#fff',
|
name: '',
|
excludeComponents: ['toolbox'],
|
pixelRatio: 1,
|
lang: ['右键另存为图片']
|
};
|
|
SaveAsImage.prototype.unusable = !env.canvasSupported;
|
|
var proto = SaveAsImage.prototype;
|
|
proto.onclick = function (ecModel, api) {
|
var model = this.model;
|
var title = model.get('name') || ecModel.get('title.0.text') || 'echarts';
|
var $a = document.createElement('a');
|
var type = model.get('type', true) || 'png';
|
$a.download = title + '.' + type;
|
$a.target = '_blank';
|
var url = api.getConnectedDataURL({
|
type: type,
|
backgroundColor: model.get('backgroundColor', true)
|
|| ecModel.get('backgroundColor') || '#fff',
|
excludeComponents: model.get('excludeComponents'),
|
pixelRatio: model.get('pixelRatio')
|
});
|
$a.href = url;
|
// Chrome and Firefox
|
if (typeof MouseEvent === 'function' && !env.browser.ie && !env.browser.edge) {
|
var evt = new MouseEvent('click', {
|
view: window,
|
bubbles: true,
|
cancelable: false
|
});
|
$a.dispatchEvent(evt);
|
}
|
// IE
|
else {
|
var lang = model.get('lang');
|
var html = ''
|
+ '<body style="margin:0;">'
|
+ '<img src="' + url + '" style="max-width:100%;" title="' + ((lang && lang[0]) || '') + '" />'
|
+ '</body>';
|
var tab = window.open();
|
tab.document.write(html);
|
}
|
};
|
|
__webpack_require__(349).register(
|
'saveAsImage', SaveAsImage
|
);
|
|
module.exports = SaveAsImage;
|
|
|
/***/ },
|
/* 408 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
|
function MagicType(model) {
|
this.model = model;
|
}
|
|
MagicType.defaultOption = {
|
show: true,
|
type: [],
|
// Icon group
|
icon: {
|
line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4',
|
bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7',
|
stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line
|
tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z'
|
},
|
title: {
|
line: '切换为折线图',
|
bar: '切换为柱状图',
|
stack: '切换为堆叠',
|
tiled: '切换为平铺'
|
},
|
option: {},
|
seriesIndex: {}
|
};
|
|
var proto = MagicType.prototype;
|
|
proto.getIcons = function () {
|
var model = this.model;
|
var availableIcons = model.get('icon');
|
var icons = {};
|
zrUtil.each(model.get('type'), function (type) {
|
if (availableIcons[type]) {
|
icons[type] = availableIcons[type];
|
}
|
});
|
return icons;
|
};
|
|
var seriesOptGenreator = {
|
'line': function (seriesType, seriesId, seriesModel, model) {
|
if (seriesType === 'bar') {
|
return zrUtil.merge({
|
id: seriesId,
|
type: 'line',
|
// Preserve data related option
|
data: seriesModel.get('data'),
|
stack: seriesModel.get('stack'),
|
markPoint: seriesModel.get('markPoint'),
|
markLine: seriesModel.get('markLine')
|
}, model.get('option.line') || {}, true);
|
}
|
},
|
'bar': function (seriesType, seriesId, seriesModel, model) {
|
if (seriesType === 'line') {
|
return zrUtil.merge({
|
id: seriesId,
|
type: 'bar',
|
// Preserve data related option
|
data: seriesModel.get('data'),
|
stack: seriesModel.get('stack'),
|
markPoint: seriesModel.get('markPoint'),
|
markLine: seriesModel.get('markLine')
|
}, model.get('option.bar') || {}, true);
|
}
|
},
|
'stack': function (seriesType, seriesId, seriesModel, model) {
|
if (seriesType === 'line' || seriesType === 'bar') {
|
return zrUtil.merge({
|
id: seriesId,
|
stack: '__ec_magicType_stack__'
|
}, model.get('option.stack') || {}, true);
|
}
|
},
|
'tiled': function (seriesType, seriesId, seriesModel, model) {
|
if (seriesType === 'line' || seriesType === 'bar') {
|
return zrUtil.merge({
|
id: seriesId,
|
stack: ''
|
}, model.get('option.tiled') || {}, true);
|
}
|
}
|
};
|
|
var radioTypes = [
|
['line', 'bar'],
|
['stack', 'tiled']
|
];
|
|
proto.onclick = function (ecModel, api, type) {
|
var model = this.model;
|
var seriesIndex = model.get('seriesIndex.' + type);
|
// Not supported magicType
|
if (!seriesOptGenreator[type]) {
|
return;
|
}
|
var newOption = {
|
series: []
|
};
|
var generateNewSeriesTypes = function (seriesModel) {
|
var seriesType = seriesModel.subType;
|
var seriesId = seriesModel.id;
|
var newSeriesOpt = seriesOptGenreator[type](
|
seriesType, seriesId, seriesModel, model
|
);
|
if (newSeriesOpt) {
|
// PENDING If merge original option?
|
zrUtil.defaults(newSeriesOpt, seriesModel.option);
|
newOption.series.push(newSeriesOpt);
|
}
|
// Modify boundaryGap
|
var coordSys = seriesModel.coordinateSystem;
|
if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) {
|
var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
|
if (categoryAxis) {
|
var axisDim = categoryAxis.dim;
|
var axisType = axisDim + 'Axis';
|
var axisModel = ecModel.queryComponents({
|
mainType: axisType,
|
index: seriesModel.get(name + 'Index'),
|
id: seriesModel.get(name + 'Id')
|
})[0];
|
var axisIndex = axisModel.componentIndex;
|
|
newOption[axisType] = newOption[axisType] || [];
|
for (var i = 0; i <= axisIndex; i++) {
|
newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {};
|
}
|
newOption[axisType][axisIndex].boundaryGap = type === 'bar' ? true : false;
|
}
|
}
|
};
|
|
zrUtil.each(radioTypes, function (radio) {
|
if (zrUtil.indexOf(radio, type) >= 0) {
|
zrUtil.each(radio, function (item) {
|
model.setIconStatus(item, 'normal');
|
});
|
}
|
});
|
|
model.setIconStatus(type, 'emphasis');
|
|
ecModel.eachComponent(
|
{
|
mainType: 'series',
|
query: seriesIndex == null ? null : {
|
seriesIndex: seriesIndex
|
}
|
}, generateNewSeriesTypes
|
);
|
api.dispatchAction({
|
type: 'changeMagicType',
|
currentType: type,
|
newOption: newOption
|
});
|
};
|
|
var echarts = __webpack_require__(1);
|
echarts.registerAction({
|
type: 'changeMagicType',
|
event: 'magicTypeChanged',
|
update: 'prepareAndUpdate'
|
}, function (payload, ecModel) {
|
ecModel.mergeOption(payload.newOption);
|
});
|
|
__webpack_require__(349).register('magicType', MagicType);
|
|
module.exports = MagicType;
|
|
|
/***/ },
|
/* 409 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @module echarts/component/toolbox/feature/DataView
|
*/
|
|
|
|
var zrUtil = __webpack_require__(4);
|
var eventTool = __webpack_require__(88);
|
|
|
var BLOCK_SPLITER = new Array(60).join('-');
|
var ITEM_SPLITER = '\t';
|
/**
|
* Group series into two types
|
* 1. on category axis, like line, bar
|
* 2. others, like scatter, pie
|
* @param {module:echarts/model/Global} ecModel
|
* @return {Object}
|
* @inner
|
*/
|
function groupSeries(ecModel) {
|
var seriesGroupByCategoryAxis = {};
|
var otherSeries = [];
|
var meta = [];
|
ecModel.eachRawSeries(function (seriesModel) {
|
var coordSys = seriesModel.coordinateSystem;
|
|
if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) {
|
var baseAxis = coordSys.getBaseAxis();
|
if (baseAxis.type === 'category') {
|
var key = baseAxis.dim + '_' + baseAxis.index;
|
if (!seriesGroupByCategoryAxis[key]) {
|
seriesGroupByCategoryAxis[key] = {
|
categoryAxis: baseAxis,
|
valueAxis: coordSys.getOtherAxis(baseAxis),
|
series: []
|
};
|
meta.push({
|
axisDim: baseAxis.dim,
|
axisIndex: baseAxis.index
|
});
|
}
|
seriesGroupByCategoryAxis[key].series.push(seriesModel);
|
}
|
else {
|
otherSeries.push(seriesModel);
|
}
|
}
|
else {
|
otherSeries.push(seriesModel);
|
}
|
});
|
|
return {
|
seriesGroupByCategoryAxis: seriesGroupByCategoryAxis,
|
other: otherSeries,
|
meta: meta
|
};
|
}
|
|
/**
|
* Assemble content of series on cateogory axis
|
* @param {Array.<module:echarts/model/Series>} series
|
* @return {string}
|
* @inner
|
*/
|
function assembleSeriesWithCategoryAxis(series) {
|
var tables = [];
|
zrUtil.each(series, function (group, key) {
|
var categoryAxis = group.categoryAxis;
|
var valueAxis = group.valueAxis;
|
var valueAxisDim = valueAxis.dim;
|
|
var headers = [' '].concat(zrUtil.map(group.series, function (series) {
|
return series.name;
|
}));
|
var columns = [categoryAxis.model.getCategories()];
|
zrUtil.each(group.series, function (series) {
|
columns.push(series.getRawData().mapArray(valueAxisDim, function (val) {
|
return val;
|
}));
|
});
|
// Assemble table content
|
var lines = [headers.join(ITEM_SPLITER)];
|
for (var i = 0; i < columns[0].length; i++) {
|
var items = [];
|
for (var j = 0; j < columns.length; j++) {
|
items.push(columns[j][i]);
|
}
|
lines.push(items.join(ITEM_SPLITER));
|
}
|
tables.push(lines.join('\n'));
|
});
|
return tables.join('\n\n' + BLOCK_SPLITER + '\n\n');
|
}
|
|
/**
|
* Assemble content of other series
|
* @param {Array.<module:echarts/model/Series>} series
|
* @return {string}
|
* @inner
|
*/
|
function assembleOtherSeries(series) {
|
return zrUtil.map(series, function (series) {
|
var data = series.getRawData();
|
var lines = [series.name];
|
var vals = [];
|
data.each(data.dimensions, function () {
|
var argLen = arguments.length;
|
var dataIndex = arguments[argLen - 1];
|
var name = data.getName(dataIndex);
|
for (var i = 0; i < argLen - 1; i++) {
|
vals[i] = arguments[i];
|
}
|
lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER));
|
});
|
return lines.join('\n');
|
}).join('\n\n' + BLOCK_SPLITER + '\n\n');
|
}
|
|
/**
|
* @param {module:echarts/model/Global}
|
* @return {string}
|
* @inner
|
*/
|
function getContentFromModel(ecModel) {
|
|
var result = groupSeries(ecModel);
|
|
return {
|
value: zrUtil.filter([
|
assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis),
|
assembleOtherSeries(result.other)
|
], function (str) {
|
return str.replace(/[\n\t\s]/g, '');
|
}).join('\n\n' + BLOCK_SPLITER + '\n\n'),
|
|
meta: result.meta
|
};
|
}
|
|
|
function trim(str) {
|
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
}
|
/**
|
* If a block is tsv format
|
*/
|
function isTSVFormat(block) {
|
// Simple method to find out if a block is tsv format
|
var firstLine = block.slice(0, block.indexOf('\n'));
|
if (firstLine.indexOf(ITEM_SPLITER) >= 0) {
|
return true;
|
}
|
}
|
|
var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g');
|
/**
|
* @param {string} tsv
|
* @return {Array.<Object>}
|
*/
|
function parseTSVContents(tsv) {
|
var tsvLines = tsv.split(/\n+/g);
|
var headers = trim(tsvLines.shift()).split(itemSplitRegex);
|
|
var categories = [];
|
var series = zrUtil.map(headers, function (header) {
|
return {
|
name: header,
|
data: []
|
};
|
});
|
for (var i = 0; i < tsvLines.length; i++) {
|
var items = trim(tsvLines[i]).split(itemSplitRegex);
|
categories.push(items.shift());
|
for (var j = 0; j < items.length; j++) {
|
series[j] && (series[j].data[i] = items[j]);
|
}
|
}
|
return {
|
series: series,
|
categories: categories
|
};
|
}
|
|
/**
|
* @param {string} str
|
* @return {Array.<Object>}
|
* @inner
|
*/
|
function parseListContents(str) {
|
var lines = str.split(/\n+/g);
|
var seriesName = trim(lines.shift());
|
|
var data = [];
|
for (var i = 0; i < lines.length; i++) {
|
var items = trim(lines[i]).split(itemSplitRegex);
|
var name = '';
|
var value;
|
var hasName = false;
|
if (isNaN(items[0])) { // First item is name
|
hasName = true;
|
name = items[0];
|
items = items.slice(1);
|
data[i] = {
|
name: name,
|
value: []
|
};
|
value = data[i].value;
|
}
|
else {
|
value = data[i] = [];
|
}
|
for (var j = 0; j < items.length; j++) {
|
value.push(+items[j]);
|
}
|
if (value.length === 1) {
|
hasName ? (data[i].value = value[0]) : (data[i] = value[0]);
|
}
|
}
|
|
return {
|
name: seriesName,
|
data: data
|
};
|
}
|
|
/**
|
* @param {string} str
|
* @param {Array.<Object>} blockMetaList
|
* @return {Object}
|
* @inner
|
*/
|
function parseContents(str, blockMetaList) {
|
var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g'));
|
var newOption = {
|
series: []
|
};
|
zrUtil.each(blocks, function (block, idx) {
|
if (isTSVFormat(block)) {
|
var result = parseTSVContents(block);
|
var blockMeta = blockMetaList[idx];
|
var axisKey = blockMeta.axisDim + 'Axis';
|
|
if (blockMeta) {
|
newOption[axisKey] = newOption[axisKey] || [];
|
newOption[axisKey][blockMeta.axisIndex] = {
|
data: result.categories
|
};
|
newOption.series = newOption.series.concat(result.series);
|
}
|
}
|
else {
|
var result = parseListContents(block);
|
newOption.series.push(result);
|
}
|
});
|
return newOption;
|
}
|
|
/**
|
* @alias {module:echarts/component/toolbox/feature/DataView}
|
* @constructor
|
* @param {module:echarts/model/Model} model
|
*/
|
function DataView(model) {
|
|
this._dom = null;
|
|
this.model = model;
|
}
|
|
DataView.defaultOption = {
|
show: true,
|
readOnly: false,
|
optionToContent: null,
|
contentToOption: null,
|
|
icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28',
|
title: '数据视图',
|
lang: ['数据视图', '关闭', '刷新'],
|
backgroundColor: '#fff',
|
textColor: '#000',
|
textareaColor: '#fff',
|
textareaBorderColor: '#333',
|
buttonColor: '#c23531',
|
buttonTextColor: '#fff'
|
};
|
|
DataView.prototype.onclick = function (ecModel, api) {
|
var container = api.getDom();
|
var model = this.model;
|
if (this._dom) {
|
container.removeChild(this._dom);
|
}
|
var root = document.createElement('div');
|
root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;';
|
root.style.backgroundColor = model.get('backgroundColor') || '#fff';
|
|
// Create elements
|
var header = document.createElement('h4');
|
var lang = model.get('lang') || [];
|
header.innerHTML = lang[0] || model.get('title');
|
header.style.cssText = 'margin: 10px 20px;';
|
header.style.color = model.get('textColor');
|
|
var viewMain = document.createElement('div');
|
var textarea = document.createElement('textarea');
|
viewMain.style.cssText = 'display:block;width:100%;overflow:auto;';
|
|
var optionToContent = model.get('optionToContent');
|
var contentToOption = model.get('contentToOption');
|
var result = getContentFromModel(ecModel);
|
if (typeof optionToContent === 'function') {
|
var htmlOrDom = optionToContent(api.getOption());
|
if (typeof htmlOrDom === 'string') {
|
viewMain.innerHTML = htmlOrDom;
|
}
|
else if (zrUtil.isDom(htmlOrDom)) {
|
viewMain.appendChild(htmlOrDom);
|
}
|
}
|
else {
|
// Use default textarea
|
viewMain.appendChild(textarea);
|
textarea.readOnly = model.get('readOnly');
|
textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;';
|
textarea.style.color = model.get('textColor');
|
textarea.style.borderColor = model.get('textareaBorderColor');
|
textarea.style.backgroundColor = model.get('textareaColor');
|
textarea.value = result.value;
|
}
|
|
var blockMetaList = result.meta;
|
|
var buttonContainer = document.createElement('div');
|
buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;';
|
|
var buttonStyle = 'float:right;margin-right:20px;border:none;'
|
+ 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px';
|
var closeButton = document.createElement('div');
|
var refreshButton = document.createElement('div');
|
|
buttonStyle += ';background-color:' + model.get('buttonColor');
|
buttonStyle += ';color:' + model.get('buttonTextColor');
|
|
var self = this;
|
|
function close() {
|
container.removeChild(root);
|
self._dom = null;
|
}
|
eventTool.addEventListener(closeButton, 'click', close);
|
|
eventTool.addEventListener(refreshButton, 'click', function () {
|
var newOption;
|
try {
|
if (typeof contentToOption === 'function') {
|
newOption = contentToOption(viewMain, api.getOption());
|
}
|
else {
|
newOption = parseContents(textarea.value, blockMetaList);
|
}
|
}
|
catch (e) {
|
close();
|
throw new Error('Data view format error ' + e);
|
}
|
if (newOption) {
|
api.dispatchAction({
|
type: 'changeDataView',
|
newOption: newOption
|
});
|
}
|
|
close();
|
});
|
|
closeButton.innerHTML = lang[1];
|
refreshButton.innerHTML = lang[2];
|
refreshButton.style.cssText = buttonStyle;
|
closeButton.style.cssText = buttonStyle;
|
|
!model.get('readOnly') && buttonContainer.appendChild(refreshButton);
|
buttonContainer.appendChild(closeButton);
|
|
// http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea
|
eventTool.addEventListener(textarea, 'keydown', function (e) {
|
if ((e.keyCode || e.which) === 9) {
|
// get caret position/selection
|
var val = this.value;
|
var start = this.selectionStart;
|
var end = this.selectionEnd;
|
|
// set textarea value to: text before caret + tab + text after caret
|
this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end);
|
|
// put caret at right position again
|
this.selectionStart = this.selectionEnd = start + 1;
|
|
// prevent the focus lose
|
eventTool.stop(e);
|
}
|
});
|
|
root.appendChild(header);
|
root.appendChild(viewMain);
|
root.appendChild(buttonContainer);
|
|
viewMain.style.height = (container.clientHeight - 80) + 'px';
|
|
container.appendChild(root);
|
this._dom = root;
|
};
|
|
DataView.prototype.remove = function (ecModel, api) {
|
this._dom && api.getDom().removeChild(this._dom);
|
};
|
|
DataView.prototype.dispose = function (ecModel, api) {
|
this.remove(ecModel, api);
|
};
|
|
/**
|
* @inner
|
*/
|
function tryMergeDataOption(newData, originalData) {
|
return zrUtil.map(newData, function (newVal, idx) {
|
var original = originalData && originalData[idx];
|
if (zrUtil.isObject(original) && !zrUtil.isArray(original)) {
|
if (zrUtil.isObject(newVal) && !zrUtil.isArray(newVal)) {
|
newVal = newVal.value;
|
}
|
// Original data has option
|
return zrUtil.defaults({
|
value: newVal
|
}, original);
|
}
|
else {
|
return newVal;
|
}
|
});
|
}
|
|
__webpack_require__(349).register('dataView', DataView);
|
|
__webpack_require__(1).registerAction({
|
type: 'changeDataView',
|
event: 'dataViewChanged',
|
update: 'prepareAndUpdate'
|
}, function (payload, ecModel) {
|
var newSeriesOptList = [];
|
zrUtil.each(payload.newOption.series, function (seriesOpt) {
|
var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0];
|
if (!seriesModel) {
|
// New created series
|
// Geuss the series type
|
newSeriesOptList.push(zrUtil.extend({
|
// Default is scatter
|
type: 'scatter'
|
}, seriesOpt));
|
}
|
else {
|
var originalData = seriesModel.get('data');
|
newSeriesOptList.push({
|
name: seriesOpt.name,
|
data: tryMergeDataOption(seriesOpt.data, originalData)
|
});
|
}
|
});
|
|
ecModel.mergeOption(zrUtil.defaults({
|
series: newSeriesOptList
|
}, payload.newOption));
|
});
|
|
module.exports = DataView;
|
|
|
/***/ },
|
/* 410 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var zrUtil = __webpack_require__(4);
|
var BrushController = __webpack_require__(244);
|
var BrushTargetManager = __webpack_require__(344);
|
var history = __webpack_require__(411);
|
|
var each = zrUtil.each;
|
|
// Use dataZoomSelect
|
__webpack_require__(412);
|
|
// Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId
|
var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_';
|
|
function DataZoom(model, ecModel, api) {
|
|
/**
|
* @private
|
* @type {module:echarts/component/helper/BrushController}
|
*/
|
(this._brushController = new BrushController(api.getZr()))
|
.on('brush', zrUtil.bind(this._onBrush, this))
|
.mount();
|
|
/**
|
* @private
|
* @type {boolean}
|
*/
|
this._isZoomActive;
|
}
|
|
DataZoom.defaultOption = {
|
show: true,
|
// Icon group
|
icon: {
|
zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1',
|
back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26'
|
},
|
title: {
|
zoom: '区域缩放',
|
back: '区域缩放还原'
|
}
|
};
|
|
var proto = DataZoom.prototype;
|
|
proto.render = function (featureModel, ecModel, api, payload) {
|
this.model = featureModel;
|
this.ecModel = ecModel;
|
this.api = api;
|
|
updateZoomBtnStatus(featureModel, ecModel, this, payload, api);
|
updateBackBtnStatus(featureModel, ecModel);
|
};
|
|
proto.onclick = function (ecModel, api, type) {
|
handlers[type].call(this);
|
};
|
|
proto.remove = function (ecModel, api) {
|
this._brushController.unmount();
|
};
|
|
proto.dispose = function (ecModel, api) {
|
this._brushController.dispose();
|
};
|
|
/**
|
* @private
|
*/
|
var handlers = {
|
|
zoom: function () {
|
var nextActive = !this._isZoomActive;
|
|
this.api.dispatchAction({
|
type: 'takeGlobalCursor',
|
key: 'dataZoomSelect',
|
dataZoomSelectActive: nextActive
|
});
|
},
|
|
back: function () {
|
this._dispatchZoomAction(history.pop(this.ecModel));
|
}
|
};
|
|
/**
|
* @private
|
*/
|
proto._onBrush = function (areas, opt) {
|
if (!opt.isEnd || !areas.length) {
|
return;
|
}
|
var snapshot = {};
|
var ecModel = this.ecModel;
|
|
this._brushController.updateCovers([]); // remove cover
|
|
var brushTargetManager = new BrushTargetManager(
|
retrieveAxisSetting(this.model.option), ecModel, {include: ['grid']}
|
);
|
brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) {
|
if (coordSys.type !== 'cartesian2d') {
|
return;
|
}
|
|
var brushType = area.brushType;
|
if (brushType === 'rect') {
|
setBatch('x', coordSys, coordRange[0]);
|
setBatch('y', coordSys, coordRange[1]);
|
}
|
else {
|
setBatch(({lineX: 'x', lineY: 'y'})[brushType], coordSys, coordRange);
|
}
|
});
|
|
history.push(ecModel, snapshot);
|
|
this._dispatchZoomAction(snapshot);
|
|
function setBatch(dimName, coordSys, minMax) {
|
var dataZoomModel = findDataZoom(dimName, coordSys.getAxis(dimName).model, ecModel);
|
dataZoomModel && (snapshot[dataZoomModel.id] = {
|
dataZoomId: dataZoomModel.id,
|
startValue: minMax[0],
|
endValue: minMax[1]
|
});
|
}
|
|
function findDataZoom(dimName, axisModel, ecModel) {
|
var found;
|
ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel) {
|
var has = dzModel.getAxisModel(dimName, axisModel.componentIndex);
|
has && (found = dzModel);
|
});
|
return found;
|
}
|
};
|
|
/**
|
* @private
|
*/
|
proto._dispatchZoomAction = function (snapshot) {
|
var batch = [];
|
|
// Convert from hash map to array.
|
each(snapshot, function (batchItem, dataZoomId) {
|
batch.push(zrUtil.clone(batchItem));
|
});
|
|
batch.length && this.api.dispatchAction({
|
type: 'dataZoom',
|
from: this.uid,
|
batch: batch
|
});
|
};
|
|
function retrieveAxisSetting(option) {
|
var setting = {};
|
// Compatible with previous setting: null => all axis, false => no axis.
|
zrUtil.each(['xAxisIndex', 'yAxisIndex'], function (name) {
|
setting[name] = option[name];
|
setting[name] == null && (setting[name] = 'all');
|
(setting[name] === false || setting[name] === 'none') && (setting[name] = []);
|
});
|
return setting;
|
}
|
|
function updateBackBtnStatus(featureModel, ecModel) {
|
featureModel.setIconStatus(
|
'back',
|
history.count(ecModel) > 1 ? 'emphasis' : 'normal'
|
);
|
}
|
|
function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) {
|
var zoomActive = view._isZoomActive;
|
|
if (payload && payload.type === 'takeGlobalCursor') {
|
zoomActive = payload.key === 'dataZoomSelect'
|
? payload.dataZoomSelectActive : false;
|
}
|
|
view._isZoomActive = zoomActive;
|
|
featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal');
|
|
var brushTargetManager = new BrushTargetManager(
|
retrieveAxisSetting(featureModel.option), ecModel, {include: ['grid']}
|
);
|
|
view._brushController
|
.setPanels(brushTargetManager.makePanelOpts(api, function (targetInfo) {
|
return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared)
|
? 'lineX'
|
: (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared)
|
? 'lineY'
|
: 'rect';
|
}))
|
.enableBrush(
|
zoomActive
|
? {
|
brushType: 'auto',
|
brushStyle: {
|
// FIXME user customized?
|
lineWidth: 0,
|
fill: 'rgba(0,0,0,0.2)'
|
}
|
}
|
: false
|
);
|
}
|
|
|
__webpack_require__(349).register('dataZoom', DataZoom);
|
|
|
// Create special dataZoom option for select
|
__webpack_require__(1).registerPreprocessor(function (option) {
|
if (!option) {
|
return;
|
}
|
|
var dataZoomOpts = option.dataZoom || (option.dataZoom = []);
|
if (!zrUtil.isArray(dataZoomOpts)) {
|
option.dataZoom = dataZoomOpts = [dataZoomOpts];
|
}
|
|
var toolboxOpt = option.toolbox;
|
if (toolboxOpt) {
|
// Assume there is only one toolbox
|
if (zrUtil.isArray(toolboxOpt)) {
|
toolboxOpt = toolboxOpt[0];
|
}
|
|
if (toolboxOpt && toolboxOpt.feature) {
|
var dataZoomOpt = toolboxOpt.feature.dataZoom;
|
addForAxis('xAxis', dataZoomOpt);
|
addForAxis('yAxis', dataZoomOpt);
|
}
|
}
|
|
function addForAxis(axisName, dataZoomOpt) {
|
if (!dataZoomOpt) {
|
return;
|
}
|
|
// Try not to modify model, because it is not merged yet.
|
var axisIndicesName = axisName + 'Index';
|
var givenAxisIndices = dataZoomOpt[axisIndicesName];
|
if (givenAxisIndices != null
|
&& givenAxisIndices != 'all'
|
&& !zrUtil.isArray(givenAxisIndices)
|
) {
|
givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices];
|
}
|
|
forEachComponent(axisName, function (axisOpt, axisIndex) {
|
if (givenAxisIndices != null
|
&& givenAxisIndices != 'all'
|
&& zrUtil.indexOf(givenAxisIndices, axisIndex) === -1
|
) {
|
return;
|
}
|
var newOpt = {
|
type: 'select',
|
$fromToolbox: true,
|
// Id for merge mapping.
|
id: DATA_ZOOM_ID_BASE + axisName + axisIndex
|
};
|
// FIXME
|
// Only support one axis now.
|
newOpt[axisIndicesName] = axisIndex;
|
dataZoomOpts.push(newOpt);
|
});
|
}
|
|
function forEachComponent(mainType, cb) {
|
var opts = option[mainType];
|
if (!zrUtil.isArray(opts)) {
|
opts = opts ? [opts] : [];
|
}
|
each(opts, cb);
|
}
|
});
|
|
module.exports = DataZoom;
|
|
|
/***/ },
|
/* 411 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file History manager.
|
*/
|
|
|
var zrUtil = __webpack_require__(4);
|
var each = zrUtil.each;
|
|
var ATTR = '\0_ec_hist_store';
|
|
var history = {
|
|
/**
|
* @public
|
* @param {module:echarts/model/Global} ecModel
|
* @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]}
|
*/
|
push: function (ecModel, newSnapshot) {
|
var store = giveStore(ecModel);
|
|
// If previous dataZoom can not be found,
|
// complete an range with current range.
|
each(newSnapshot, function (batchItem, dataZoomId) {
|
var i = store.length - 1;
|
for (; i >= 0; i--) {
|
var snapshot = store[i];
|
if (snapshot[dataZoomId]) {
|
break;
|
}
|
}
|
if (i < 0) {
|
// No origin range set, create one by current range.
|
var dataZoomModel = ecModel.queryComponents(
|
{mainType: 'dataZoom', subType: 'select', id: dataZoomId}
|
)[0];
|
if (dataZoomModel) {
|
var percentRange = dataZoomModel.getPercentRange();
|
store[0][dataZoomId] = {
|
dataZoomId: dataZoomId,
|
start: percentRange[0],
|
end: percentRange[1]
|
};
|
}
|
}
|
});
|
|
store.push(newSnapshot);
|
},
|
|
/**
|
* @public
|
* @param {module:echarts/model/Global} ecModel
|
* @return {Object} snapshot
|
*/
|
pop: function (ecModel) {
|
var store = giveStore(ecModel);
|
var head = store[store.length - 1];
|
store.length > 1 && store.pop();
|
|
// Find top for all dataZoom.
|
var snapshot = {};
|
each(head, function (batchItem, dataZoomId) {
|
for (var i = store.length - 1; i >= 0; i--) {
|
var batchItem = store[i][dataZoomId];
|
if (batchItem) {
|
snapshot[dataZoomId] = batchItem;
|
break;
|
}
|
}
|
});
|
|
return snapshot;
|
},
|
|
/**
|
* @public
|
*/
|
clear: function (ecModel) {
|
ecModel[ATTR] = null;
|
},
|
|
/**
|
* @public
|
* @param {module:echarts/model/Global} ecModel
|
* @return {number} records. always >= 1.
|
*/
|
count: function (ecModel) {
|
return giveStore(ecModel).length;
|
}
|
|
};
|
|
/**
|
* [{key: dataZoomId, value: {dataZoomId, range}}, ...]
|
* History length of each dataZoom may be different.
|
* this._history[0] is used to store origin range.
|
* @type {Array.<Object>}
|
*/
|
function giveStore(ecModel) {
|
var store = ecModel[ATTR];
|
if (!store) {
|
store = ecModel[ATTR] = [{}];
|
}
|
return store;
|
}
|
|
module.exports = history;
|
|
|
|
/***/ },
|
/* 412 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* DataZoom component entry
|
*/
|
|
|
__webpack_require__(356);
|
|
__webpack_require__(357);
|
__webpack_require__(360);
|
|
__webpack_require__(413);
|
__webpack_require__(414);
|
|
__webpack_require__(366);
|
__webpack_require__(367);
|
|
|
|
/***/ },
|
/* 413 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* @file Data zoom model
|
*/
|
|
|
var DataZoomModel = __webpack_require__(357);
|
|
module.exports = DataZoomModel.extend({
|
|
type: 'dataZoom.select'
|
|
});
|
|
|
|
/***/ },
|
/* 414 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__(360).extend({
|
|
type: 'dataZoom.select'
|
|
});
|
|
|
|
/***/ },
|
/* 415 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
'use strict';
|
|
|
var history = __webpack_require__(411);
|
|
function Restore(model) {
|
this.model = model;
|
}
|
|
Restore.defaultOption = {
|
show: true,
|
icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5',
|
title: '还原'
|
};
|
|
var proto = Restore.prototype;
|
|
proto.onclick = function (ecModel, api, type) {
|
history.clear(ecModel);
|
|
api.dispatchAction({
|
type: 'restore',
|
from: this.uid
|
});
|
};
|
|
|
__webpack_require__(349).register('restore', Restore);
|
|
|
__webpack_require__(1).registerAction(
|
{type: 'restore', event: 'restore', update: 'prepareAndUpdate'},
|
function (payload, ecModel) {
|
ecModel.resetOption('recreate');
|
}
|
);
|
|
module.exports = Restore;
|
|
|
/***/ },
|
/* 416 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
__webpack_require__(417);
|
__webpack_require__(82).registerPainter('vml', __webpack_require__(419));
|
|
|
/***/ },
|
/* 417 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
// http://www.w3.org/TR/NOTE-VML
|
// TODO Use proxy like svg instead of overwrite brush methods
|
|
|
if (!__webpack_require__(2).canvasSupported) {
|
var vec2 = __webpack_require__(10);
|
var BoundingRect = __webpack_require__(9);
|
var CMD = __webpack_require__(50).CMD;
|
var colorTool = __webpack_require__(39);
|
var textContain = __webpack_require__(8);
|
var RectText = __webpack_require__(49);
|
var Displayable = __webpack_require__(47);
|
var ZImage = __webpack_require__(62);
|
var Text = __webpack_require__(63);
|
var Path = __webpack_require__(46);
|
var PathProxy = __webpack_require__(50);
|
|
var Gradient = __webpack_require__(79);
|
|
var vmlCore = __webpack_require__(418);
|
|
var round = Math.round;
|
var sqrt = Math.sqrt;
|
var abs = Math.abs;
|
var cos = Math.cos;
|
var sin = Math.sin;
|
var mathMax = Math.max;
|
|
var applyTransform = vec2.applyTransform;
|
|
var comma = ',';
|
var imageTransformPrefix = 'progid:DXImageTransform.Microsoft';
|
|
var Z = 21600;
|
var Z2 = Z / 2;
|
|
var ZLEVEL_BASE = 100000;
|
var Z_BASE = 1000;
|
|
var initRootElStyle = function (el) {
|
el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;';
|
el.coordsize = Z + ',' + Z;
|
el.coordorigin = '0,0';
|
};
|
|
var encodeHtmlAttribute = function (s) {
|
return String(s).replace(/&/g, '&').replace(/"/g, '"');
|
};
|
|
var rgb2Str = function (r, g, b) {
|
return 'rgb(' + [r, g, b].join(',') + ')';
|
};
|
|
var append = function (parent, child) {
|
if (child && parent && child.parentNode !== parent) {
|
parent.appendChild(child);
|
}
|
};
|
|
var remove = function (parent, child) {
|
if (child && parent && child.parentNode === parent) {
|
parent.removeChild(child);
|
}
|
};
|
|
var getZIndex = function (zlevel, z, z2) {
|
// z 的取值范围为 [0, 1000]
|
return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2;
|
};
|
|
var parsePercent = function (value, maxValue) {
|
if (typeof value === 'string') {
|
if (value.lastIndexOf('%') >= 0) {
|
return parseFloat(value) / 100 * maxValue;
|
}
|
return parseFloat(value);
|
}
|
return value;
|
};
|
|
/***************************************************
|
* PATH
|
**************************************************/
|
|
var setColorAndOpacity = function (el, color, opacity) {
|
var colorArr = colorTool.parse(color);
|
opacity = +opacity;
|
if (isNaN(opacity)) {
|
opacity = 1;
|
}
|
if (colorArr) {
|
el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]);
|
el.opacity = opacity * colorArr[3];
|
}
|
};
|
|
var getColorAndAlpha = function (color) {
|
var colorArr = colorTool.parse(color);
|
return [
|
rgb2Str(colorArr[0], colorArr[1], colorArr[2]),
|
colorArr[3]
|
];
|
};
|
|
var updateFillNode = function (el, style, zrEl) {
|
// TODO pattern
|
var fill = style.fill;
|
if (fill != null) {
|
// Modified from excanvas
|
if (fill instanceof Gradient) {
|
var gradientType;
|
var angle = 0;
|
var focus = [0, 0];
|
// additional offset
|
var shift = 0;
|
// scale factor for offset
|
var expansion = 1;
|
var rect = zrEl.getBoundingRect();
|
var rectWidth = rect.width;
|
var rectHeight = rect.height;
|
if (fill.type === 'linear') {
|
gradientType = 'gradient';
|
var transform = zrEl.transform;
|
var p0 = [fill.x * rectWidth, fill.y * rectHeight];
|
var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight];
|
if (transform) {
|
applyTransform(p0, p0, transform);
|
applyTransform(p1, p1, transform);
|
}
|
var dx = p1[0] - p0[0];
|
var dy = p1[1] - p0[1];
|
angle = Math.atan2(dx, dy) * 180 / Math.PI;
|
// The angle should be a non-negative number.
|
if (angle < 0) {
|
angle += 360;
|
}
|
|
// Very small angles produce an unexpected result because they are
|
// converted to a scientific notation string.
|
if (angle < 1e-6) {
|
angle = 0;
|
}
|
}
|
else {
|
gradientType = 'gradientradial';
|
var p0 = [fill.x * rectWidth, fill.y * rectHeight];
|
var transform = zrEl.transform;
|
var scale = zrEl.scale;
|
var width = rectWidth;
|
var height = rectHeight;
|
focus = [
|
// Percent in bounding rect
|
(p0[0] - rect.x) / width,
|
(p0[1] - rect.y) / height
|
];
|
if (transform) {
|
applyTransform(p0, p0, transform);
|
}
|
|
width /= scale[0] * Z;
|
height /= scale[1] * Z;
|
var dimension = mathMax(width, height);
|
shift = 2 * 0 / dimension;
|
expansion = 2 * fill.r / dimension - shift;
|
}
|
|
// We need to sort the color stops in ascending order by offset,
|
// otherwise IE won't interpret it correctly.
|
var stops = fill.colorStops.slice();
|
stops.sort(function(cs1, cs2) {
|
return cs1.offset - cs2.offset;
|
});
|
|
var length = stops.length;
|
// Color and alpha list of first and last stop
|
var colorAndAlphaList = [];
|
var colors = [];
|
for (var i = 0; i < length; i++) {
|
var stop = stops[i];
|
var colorAndAlpha = getColorAndAlpha(stop.color);
|
colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]);
|
if (i === 0 || i === length - 1) {
|
colorAndAlphaList.push(colorAndAlpha);
|
}
|
}
|
|
if (length >= 2) {
|
var color1 = colorAndAlphaList[0][0];
|
var color2 = colorAndAlphaList[1][0];
|
var opacity1 = colorAndAlphaList[0][1] * style.opacity;
|
var opacity2 = colorAndAlphaList[1][1] * style.opacity;
|
|
el.type = gradientType;
|
el.method = 'none';
|
el.focus = '100%';
|
el.angle = angle;
|
el.color = color1;
|
el.color2 = color2;
|
el.colors = colors.join(',');
|
// When colors attribute is used, the meanings of opacity and o:opacity2
|
// are reversed.
|
el.opacity = opacity2;
|
// FIXME g_o_:opacity ?
|
el.opacity2 = opacity1;
|
}
|
if (gradientType === 'radial') {
|
el.focusposition = focus.join(',');
|
}
|
}
|
else {
|
// FIXME Change from Gradient fill to color fill
|
setColorAndOpacity(el, fill, style.opacity);
|
}
|
}
|
};
|
|
var updateStrokeNode = function (el, style) {
|
// if (style.lineJoin != null) {
|
// el.joinstyle = style.lineJoin;
|
// }
|
// if (style.miterLimit != null) {
|
// el.miterlimit = style.miterLimit * Z;
|
// }
|
// if (style.lineCap != null) {
|
// el.endcap = style.lineCap;
|
// }
|
if (style.lineDash != null) {
|
el.dashstyle = style.lineDash.join(' ');
|
}
|
if (style.stroke != null && !(style.stroke instanceof Gradient)) {
|
setColorAndOpacity(el, style.stroke, style.opacity);
|
}
|
};
|
|
var updateFillAndStroke = function (vmlEl, type, style, zrEl) {
|
var isFill = type == 'fill';
|
var el = vmlEl.getElementsByTagName(type)[0];
|
// Stroke must have lineWidth
|
if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) {
|
vmlEl[isFill ? 'filled' : 'stroked'] = 'true';
|
// FIXME Remove before updating, or set `colors` will throw error
|
if (style[type] instanceof Gradient) {
|
remove(vmlEl, el);
|
}
|
if (!el) {
|
el = vmlCore.createNode(type);
|
}
|
|
isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style);
|
append(vmlEl, el);
|
}
|
else {
|
vmlEl[isFill ? 'filled' : 'stroked'] = 'false';
|
remove(vmlEl, el);
|
}
|
};
|
|
var points = [[], [], []];
|
var pathDataToString = function (data, m) {
|
var M = CMD.M;
|
var C = CMD.C;
|
var L = CMD.L;
|
var A = CMD.A;
|
var Q = CMD.Q;
|
|
var str = [];
|
var nPoint;
|
var cmdStr;
|
var cmd;
|
var i;
|
var xi;
|
var yi;
|
for (i = 0; i < data.length;) {
|
cmd = data[i++];
|
cmdStr = '';
|
nPoint = 0;
|
switch (cmd) {
|
case M:
|
cmdStr = ' m ';
|
nPoint = 1;
|
xi = data[i++];
|
yi = data[i++];
|
points[0][0] = xi;
|
points[0][1] = yi;
|
break;
|
case L:
|
cmdStr = ' l ';
|
nPoint = 1;
|
xi = data[i++];
|
yi = data[i++];
|
points[0][0] = xi;
|
points[0][1] = yi;
|
break;
|
case Q:
|
case C:
|
cmdStr = ' c ';
|
nPoint = 3;
|
var x1 = data[i++];
|
var y1 = data[i++];
|
var x2 = data[i++];
|
var y2 = data[i++];
|
var x3;
|
var y3;
|
if (cmd === Q) {
|
// Convert quadratic to cubic using degree elevation
|
x3 = x2;
|
y3 = y2;
|
x2 = (x2 + 2 * x1) / 3;
|
y2 = (y2 + 2 * y1) / 3;
|
x1 = (xi + 2 * x1) / 3;
|
y1 = (yi + 2 * y1) / 3;
|
}
|
else {
|
x3 = data[i++];
|
y3 = data[i++];
|
}
|
points[0][0] = x1;
|
points[0][1] = y1;
|
points[1][0] = x2;
|
points[1][1] = y2;
|
points[2][0] = x3;
|
points[2][1] = y3;
|
|
xi = x3;
|
yi = y3;
|
break;
|
case A:
|
var x = 0;
|
var y = 0;
|
var sx = 1;
|
var sy = 1;
|
var angle = 0;
|
if (m) {
|
// Extract SRT from matrix
|
x = m[4];
|
y = m[5];
|
sx = sqrt(m[0] * m[0] + m[1] * m[1]);
|
sy = sqrt(m[2] * m[2] + m[3] * m[3]);
|
angle = Math.atan2(-m[1] / sy, m[0] / sx);
|
}
|
|
var cx = data[i++];
|
var cy = data[i++];
|
var rx = data[i++];
|
var ry = data[i++];
|
var startAngle = data[i++] + angle;
|
var endAngle = data[i++] + startAngle + angle;
|
// FIXME
|
// var psi = data[i++];
|
i++;
|
var clockwise = data[i++];
|
|
var x0 = cx + cos(startAngle) * rx;
|
var y0 = cy + sin(startAngle) * ry;
|
|
var x1 = cx + cos(endAngle) * rx;
|
var y1 = cy + sin(endAngle) * ry;
|
|
var type = clockwise ? ' wa ' : ' at ';
|
if (Math.abs(x0 - x1) < 1e-4) {
|
// IE won't render arches drawn counter clockwise if x0 == x1.
|
if (Math.abs(endAngle - startAngle) > 1e-2) {
|
// Offset x0 by 1/80 of a pixel. Use something
|
// that can be represented in binary
|
if (clockwise) {
|
x0 += 270 / Z;
|
}
|
}
|
else {
|
// Avoid case draw full circle
|
if (Math.abs(y0 - cy) < 1e-4) {
|
if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) {
|
y1 -= 270 / Z;
|
}
|
else {
|
y1 += 270 / Z;
|
}
|
}
|
else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) {
|
x1 += 270 / Z;
|
}
|
else {
|
x1 -= 270 / Z;
|
}
|
}
|
}
|
str.push(
|
type,
|
round(((cx - rx) * sx + x) * Z - Z2), comma,
|
round(((cy - ry) * sy + y) * Z - Z2), comma,
|
round(((cx + rx) * sx + x) * Z - Z2), comma,
|
round(((cy + ry) * sy + y) * Z - Z2), comma,
|
round((x0 * sx + x) * Z - Z2), comma,
|
round((y0 * sy + y) * Z - Z2), comma,
|
round((x1 * sx + x) * Z - Z2), comma,
|
round((y1 * sy + y) * Z - Z2)
|
);
|
|
xi = x1;
|
yi = y1;
|
break;
|
case CMD.R:
|
var p0 = points[0];
|
var p1 = points[1];
|
// x0, y0
|
p0[0] = data[i++];
|
p0[1] = data[i++];
|
// x1, y1
|
p1[0] = p0[0] + data[i++];
|
p1[1] = p0[1] + data[i++];
|
|
if (m) {
|
applyTransform(p0, p0, m);
|
applyTransform(p1, p1, m);
|
}
|
|
p0[0] = round(p0[0] * Z - Z2);
|
p1[0] = round(p1[0] * Z - Z2);
|
p0[1] = round(p0[1] * Z - Z2);
|
p1[1] = round(p1[1] * Z - Z2);
|
str.push(
|
// x0, y0
|
' m ', p0[0], comma, p0[1],
|
// x1, y0
|
' l ', p1[0], comma, p0[1],
|
// x1, y1
|
' l ', p1[0], comma, p1[1],
|
// x0, y1
|
' l ', p0[0], comma, p1[1]
|
);
|
break;
|
case CMD.Z:
|
// FIXME Update xi, yi
|
str.push(' x ');
|
}
|
|
if (nPoint > 0) {
|
str.push(cmdStr);
|
for (var k = 0; k < nPoint; k++) {
|
var p = points[k];
|
|
m && applyTransform(p, p, m);
|
// 不 round 会非常慢
|
str.push(
|
round(p[0] * Z - Z2), comma, round(p[1] * Z - Z2),
|
k < nPoint - 1 ? comma : ''
|
);
|
}
|
}
|
}
|
|
return str.join('');
|
};
|
|
// Rewrite the original path method
|
Path.prototype.brushVML = function (vmlRoot) {
|
var style = this.style;
|
|
var vmlEl = this._vmlEl;
|
if (!vmlEl) {
|
vmlEl = vmlCore.createNode('shape');
|
initRootElStyle(vmlEl);
|
|
this._vmlEl = vmlEl;
|
}
|
|
updateFillAndStroke(vmlEl, 'fill', style, this);
|
updateFillAndStroke(vmlEl, 'stroke', style, this);
|
|
var m = this.transform;
|
var needTransform = m != null;
|
var strokeEl = vmlEl.getElementsByTagName('stroke')[0];
|
if (strokeEl) {
|
var lineWidth = style.lineWidth;
|
// Get the line scale.
|
// Determinant of this.m_ means how much the area is enlarged by the
|
// transformation. So its square root can be used as a scale factor
|
// for width.
|
if (needTransform && !style.strokeNoScale) {
|
var det = m[0] * m[3] - m[1] * m[2];
|
lineWidth *= sqrt(abs(det));
|
}
|
strokeEl.weight = lineWidth + 'px';
|
}
|
|
var path = this.path || (this.path = new PathProxy());
|
if (this.__dirtyPath) {
|
path.beginPath();
|
this.buildPath(path, this.shape);
|
path.toStatic();
|
this.__dirtyPath = false;
|
}
|
|
vmlEl.path = pathDataToString(path.data, this.transform);
|
|
vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
|
|
// Append to root
|
append(vmlRoot, vmlEl);
|
|
// Text
|
if (style.text != null) {
|
this.drawRectText(vmlRoot, this.getBoundingRect());
|
}
|
else {
|
this.removeRectText(vmlRoot);
|
}
|
};
|
|
Path.prototype.onRemove = function (vmlRoot) {
|
remove(vmlRoot, this._vmlEl);
|
this.removeRectText(vmlRoot);
|
};
|
|
Path.prototype.onAdd = function (vmlRoot) {
|
append(vmlRoot, this._vmlEl);
|
this.appendRectText(vmlRoot);
|
};
|
|
/***************************************************
|
* IMAGE
|
**************************************************/
|
var isImage = function (img) {
|
// FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错
|
return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG';
|
// return img instanceof Image;
|
};
|
|
// Rewrite the original path method
|
ZImage.prototype.brushVML = function (vmlRoot) {
|
var style = this.style;
|
var image = style.image;
|
|
// Image original width, height
|
var ow;
|
var oh;
|
|
if (isImage(image)) {
|
var src = image.src;
|
if (src === this._imageSrc) {
|
ow = this._imageWidth;
|
oh = this._imageHeight;
|
}
|
else {
|
var imageRuntimeStyle = image.runtimeStyle;
|
var oldRuntimeWidth = imageRuntimeStyle.width;
|
var oldRuntimeHeight = imageRuntimeStyle.height;
|
imageRuntimeStyle.width = 'auto';
|
imageRuntimeStyle.height = 'auto';
|
|
// get the original size
|
ow = image.width;
|
oh = image.height;
|
|
// and remove overides
|
imageRuntimeStyle.width = oldRuntimeWidth;
|
imageRuntimeStyle.height = oldRuntimeHeight;
|
|
// Caching image original width, height and src
|
this._imageSrc = src;
|
this._imageWidth = ow;
|
this._imageHeight = oh;
|
}
|
image = src;
|
}
|
else {
|
if (image === this._imageSrc) {
|
ow = this._imageWidth;
|
oh = this._imageHeight;
|
}
|
}
|
if (!image) {
|
return;
|
}
|
|
var x = style.x || 0;
|
var y = style.y || 0;
|
|
var dw = style.width;
|
var dh = style.height;
|
|
var sw = style.sWidth;
|
var sh = style.sHeight;
|
var sx = style.sx || 0;
|
var sy = style.sy || 0;
|
|
var hasCrop = sw && sh;
|
|
var vmlEl = this._vmlEl;
|
if (!vmlEl) {
|
// FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。
|
// vmlEl = vmlCore.createNode('group');
|
vmlEl = vmlCore.doc.createElement('div');
|
initRootElStyle(vmlEl);
|
|
this._vmlEl = vmlEl;
|
}
|
|
var vmlElStyle = vmlEl.style;
|
var hasRotation = false;
|
var m;
|
var scaleX = 1;
|
var scaleY = 1;
|
if (this.transform) {
|
m = this.transform;
|
scaleX = sqrt(m[0] * m[0] + m[1] * m[1]);
|
scaleY = sqrt(m[2] * m[2] + m[3] * m[3]);
|
|
hasRotation = m[1] || m[2];
|
}
|
if (hasRotation) {
|
// If filters are necessary (rotation exists), create them
|
// filters are bog-slow, so only create them if abbsolutely necessary
|
// The following check doesn't account for skews (which don't exist
|
// in the canvas spec (yet) anyway.
|
// From excanvas
|
var p0 = [x, y];
|
var p1 = [x + dw, y];
|
var p2 = [x, y + dh];
|
var p3 = [x + dw, y + dh];
|
applyTransform(p0, p0, m);
|
applyTransform(p1, p1, m);
|
applyTransform(p2, p2, m);
|
applyTransform(p3, p3, m);
|
|
var maxX = mathMax(p0[0], p1[0], p2[0], p3[0]);
|
var maxY = mathMax(p0[1], p1[1], p2[1], p3[1]);
|
|
var transformFilter = [];
|
transformFilter.push('M11=', m[0] / scaleX, comma,
|
'M12=', m[2] / scaleY, comma,
|
'M21=', m[1] / scaleX, comma,
|
'M22=', m[3] / scaleY, comma,
|
'Dx=', round(x * scaleX + m[4]), comma,
|
'Dy=', round(y * scaleY + m[5]));
|
|
vmlElStyle.padding = '0 ' + round(maxX) + 'px ' + round(maxY) + 'px 0';
|
// FIXME DXImageTransform 在 IE11 的兼容模式下不起作用
|
vmlElStyle.filter = imageTransformPrefix + '.Matrix('
|
+ transformFilter.join('') + ', SizingMethod=clip)';
|
|
}
|
else {
|
if (m) {
|
x = x * scaleX + m[4];
|
y = y * scaleY + m[5];
|
}
|
vmlElStyle.filter = '';
|
vmlElStyle.left = round(x) + 'px';
|
vmlElStyle.top = round(y) + 'px';
|
}
|
|
var imageEl = this._imageEl;
|
var cropEl = this._cropEl;
|
|
if (!imageEl) {
|
imageEl = vmlCore.doc.createElement('div');
|
this._imageEl = imageEl;
|
}
|
var imageELStyle = imageEl.style;
|
if (hasCrop) {
|
// Needs know image original width and height
|
if (! (ow && oh)) {
|
var tmpImage = new Image();
|
var self = this;
|
tmpImage.onload = function () {
|
tmpImage.onload = null;
|
ow = tmpImage.width;
|
oh = tmpImage.height;
|
// Adjust image width and height to fit the ratio destinationSize / sourceSize
|
imageELStyle.width = round(scaleX * ow * dw / sw) + 'px';
|
imageELStyle.height = round(scaleY * oh * dh / sh) + 'px';
|
|
// Caching image original width, height and src
|
self._imageWidth = ow;
|
self._imageHeight = oh;
|
self._imageSrc = image;
|
};
|
tmpImage.src = image;
|
}
|
else {
|
imageELStyle.width = round(scaleX * ow * dw / sw) + 'px';
|
imageELStyle.height = round(scaleY * oh * dh / sh) + 'px';
|
}
|
|
if (! cropEl) {
|
cropEl = vmlCore.doc.createElement('div');
|
cropEl.style.overflow = 'hidden';
|
this._cropEl = cropEl;
|
}
|
var cropElStyle = cropEl.style;
|
cropElStyle.width = round((dw + sx * dw / sw) * scaleX);
|
cropElStyle.height = round((dh + sy * dh / sh) * scaleY);
|
cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx='
|
+ (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')';
|
|
if (! cropEl.parentNode) {
|
vmlEl.appendChild(cropEl);
|
}
|
if (imageEl.parentNode != cropEl) {
|
cropEl.appendChild(imageEl);
|
}
|
}
|
else {
|
imageELStyle.width = round(scaleX * dw) + 'px';
|
imageELStyle.height = round(scaleY * dh) + 'px';
|
|
vmlEl.appendChild(imageEl);
|
|
if (cropEl && cropEl.parentNode) {
|
vmlEl.removeChild(cropEl);
|
this._cropEl = null;
|
}
|
}
|
|
var filterStr = '';
|
var alpha = style.opacity;
|
if (alpha < 1) {
|
filterStr += '.Alpha(opacity=' + round(alpha * 100) + ') ';
|
}
|
filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)';
|
|
imageELStyle.filter = filterStr;
|
|
vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
|
|
// Append to root
|
append(vmlRoot, vmlEl);
|
|
// Text
|
if (style.text != null) {
|
this.drawRectText(vmlRoot, this.getBoundingRect());
|
}
|
};
|
|
ZImage.prototype.onRemove = function (vmlRoot) {
|
remove(vmlRoot, this._vmlEl);
|
|
this._vmlEl = null;
|
this._cropEl = null;
|
this._imageEl = null;
|
|
this.removeRectText(vmlRoot);
|
};
|
|
ZImage.prototype.onAdd = function (vmlRoot) {
|
append(vmlRoot, this._vmlEl);
|
this.appendRectText(vmlRoot);
|
};
|
|
|
/***************************************************
|
* TEXT
|
**************************************************/
|
|
var DEFAULT_STYLE_NORMAL = 'normal';
|
|
var fontStyleCache = {};
|
var fontStyleCacheCount = 0;
|
var MAX_FONT_CACHE_SIZE = 100;
|
var fontEl = document.createElement('div');
|
|
var getFontStyle = function (fontString) {
|
var fontStyle = fontStyleCache[fontString];
|
if (!fontStyle) {
|
// Clear cache
|
if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) {
|
fontStyleCacheCount = 0;
|
fontStyleCache = {};
|
}
|
|
var style = fontEl.style;
|
var fontFamily;
|
try {
|
style.font = fontString;
|
fontFamily = style.fontFamily.split(',')[0];
|
}
|
catch (e) {
|
}
|
|
fontStyle = {
|
style: style.fontStyle || DEFAULT_STYLE_NORMAL,
|
variant: style.fontVariant || DEFAULT_STYLE_NORMAL,
|
weight: style.fontWeight || DEFAULT_STYLE_NORMAL,
|
size: parseFloat(style.fontSize || 12) | 0,
|
family: fontFamily || 'Microsoft YaHei'
|
};
|
|
fontStyleCache[fontString] = fontStyle;
|
fontStyleCacheCount++;
|
}
|
return fontStyle;
|
};
|
|
var textMeasureEl;
|
// Overwrite measure text method
|
textContain.measureText = function (text, textFont) {
|
var doc = vmlCore.doc;
|
if (!textMeasureEl) {
|
textMeasureEl = doc.createElement('div');
|
textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;'
|
+ 'padding:0;margin:0;border:none;white-space:pre;';
|
vmlCore.doc.body.appendChild(textMeasureEl);
|
}
|
|
try {
|
textMeasureEl.style.font = textFont;
|
} catch (ex) {
|
// Ignore failures to set to invalid font.
|
}
|
textMeasureEl.innerHTML = '';
|
// Don't use innerHTML or innerText because they allow markup/whitespace.
|
textMeasureEl.appendChild(doc.createTextNode(text));
|
return {
|
width: textMeasureEl.offsetWidth
|
};
|
};
|
|
var tmpRect = new BoundingRect();
|
|
var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) {
|
|
var style = this.style;
|
var text = style.text;
|
// Convert to string
|
text != null && (text += '');
|
if (!text) {
|
return;
|
}
|
|
var x;
|
var y;
|
var align = style.textAlign;
|
var fontStyle = getFontStyle(style.textFont);
|
// FIXME encodeHtmlAttribute ?
|
var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' '
|
+ fontStyle.size + 'px "' + fontStyle.family + '"';
|
|
var baseline = style.textBaseline;
|
var verticalAlign = style.textVerticalAlign;
|
|
textRect = textRect || textContain.getBoundingRect(text, font, align, baseline);
|
|
// Transform rect to view space
|
var m = this.transform;
|
// Ignore transform for text in other element
|
if (m && !fromTextEl) {
|
tmpRect.copy(rect);
|
tmpRect.applyTransform(m);
|
rect = tmpRect;
|
}
|
|
if (!fromTextEl) {
|
var textPosition = style.textPosition;
|
var distance = style.textDistance;
|
// Text position represented by coord
|
if (textPosition instanceof Array) {
|
x = rect.x + parsePercent(textPosition[0], rect.width);
|
y = rect.y + parsePercent(textPosition[1], rect.height);
|
|
align = align || 'left';
|
baseline = baseline || 'top';
|
}
|
else {
|
var res = textContain.adjustTextPositionOnRect(
|
textPosition, rect, textRect, distance
|
);
|
x = res.x;
|
y = res.y;
|
|
// Default align and baseline when has textPosition
|
align = align || res.textAlign;
|
baseline = baseline || res.textBaseline;
|
}
|
}
|
else {
|
x = rect.x;
|
y = rect.y;
|
}
|
if (verticalAlign) {
|
switch (verticalAlign) {
|
case 'middle':
|
y -= textRect.height / 2;
|
break;
|
case 'bottom':
|
y -= textRect.height;
|
break;
|
// 'top'
|
}
|
// Ignore baseline
|
baseline = 'top';
|
}
|
|
var fontSize = fontStyle.size;
|
// 1.75 is an arbitrary number, as there is no info about the text baseline
|
switch (baseline) {
|
case 'hanging':
|
case 'top':
|
y += fontSize / 1.75;
|
break;
|
case 'middle':
|
break;
|
default:
|
// case null:
|
// case 'alphabetic':
|
// case 'ideographic':
|
// case 'bottom':
|
y -= fontSize / 2.25;
|
break;
|
}
|
switch (align) {
|
case 'left':
|
break;
|
case 'center':
|
x -= textRect.width / 2;
|
break;
|
case 'right':
|
x -= textRect.width;
|
break;
|
// case 'end':
|
// align = elementStyle.direction == 'ltr' ? 'right' : 'left';
|
// break;
|
// case 'start':
|
// align = elementStyle.direction == 'rtl' ? 'right' : 'left';
|
// break;
|
// default:
|
// align = 'left';
|
}
|
|
var createNode = vmlCore.createNode;
|
|
var textVmlEl = this._textVmlEl;
|
var pathEl;
|
var textPathEl;
|
var skewEl;
|
if (!textVmlEl) {
|
textVmlEl = createNode('line');
|
pathEl = createNode('path');
|
textPathEl = createNode('textpath');
|
skewEl = createNode('skew');
|
|
// FIXME Why here is not cammel case
|
// Align 'center' seems wrong
|
textPathEl.style['v-text-align'] = 'left';
|
|
initRootElStyle(textVmlEl);
|
|
pathEl.textpathok = true;
|
textPathEl.on = true;
|
|
textVmlEl.from = '0 0';
|
textVmlEl.to = '1000 0.05';
|
|
append(textVmlEl, skewEl);
|
append(textVmlEl, pathEl);
|
append(textVmlEl, textPathEl);
|
|
this._textVmlEl = textVmlEl;
|
}
|
else {
|
// 这里是在前面 appendChild 保证顺序的前提下
|
skewEl = textVmlEl.firstChild;
|
pathEl = skewEl.nextSibling;
|
textPathEl = pathEl.nextSibling;
|
}
|
|
var coords = [x, y];
|
var textVmlElStyle = textVmlEl.style;
|
// Ignore transform for text in other element
|
if (m && fromTextEl) {
|
applyTransform(coords, coords, m);
|
|
skewEl.on = true;
|
|
skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma +
|
m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0';
|
|
// Text position
|
skewEl.offset = (round(coords[0]) || 0) + ',' + (round(coords[1]) || 0);
|
// Left top point as origin
|
skewEl.origin = '0 0';
|
|
textVmlElStyle.left = '0px';
|
textVmlElStyle.top = '0px';
|
}
|
else {
|
skewEl.on = false;
|
textVmlElStyle.left = round(x) + 'px';
|
textVmlElStyle.top = round(y) + 'px';
|
}
|
|
textPathEl.string = encodeHtmlAttribute(text);
|
// TODO
|
try {
|
textPathEl.style.font = font;
|
}
|
// Error font format
|
catch (e) {}
|
|
updateFillAndStroke(textVmlEl, 'fill', {
|
fill: fromTextEl ? style.fill : style.textFill,
|
opacity: style.opacity
|
}, this);
|
updateFillAndStroke(textVmlEl, 'stroke', {
|
stroke: fromTextEl ? style.stroke : style.textStroke,
|
opacity: style.opacity,
|
lineDash: style.lineDash
|
}, this);
|
|
textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2);
|
|
// Attached to root
|
append(vmlRoot, textVmlEl);
|
};
|
|
var removeRectText = function (vmlRoot) {
|
remove(vmlRoot, this._textVmlEl);
|
this._textVmlEl = null;
|
};
|
|
var appendRectText = function (vmlRoot) {
|
append(vmlRoot, this._textVmlEl);
|
};
|
|
var list = [RectText, Displayable, ZImage, Path, Text];
|
|
// In case Displayable has been mixed in RectText
|
for (var i = 0; i < list.length; i++) {
|
var proto = list[i].prototype;
|
proto.drawRectText = drawRectText;
|
proto.removeRectText = removeRectText;
|
proto.appendRectText = appendRectText;
|
}
|
|
Text.prototype.brushVML = function (vmlRoot) {
|
var style = this.style;
|
if (style.text != null) {
|
this.drawRectText(vmlRoot, {
|
x: style.x || 0, y: style.y || 0,
|
width: 0, height: 0
|
}, this.getBoundingRect(), true);
|
}
|
else {
|
this.removeRectText(vmlRoot);
|
}
|
};
|
|
Text.prototype.onRemove = function (vmlRoot) {
|
this.removeRectText(vmlRoot);
|
};
|
|
Text.prototype.onAdd = function (vmlRoot) {
|
this.appendRectText(vmlRoot);
|
};
|
}
|
|
|
/***/ },
|
/* 418 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
|
|
if (!__webpack_require__(2).canvasSupported) {
|
var urn = 'urn:schemas-microsoft-com:vml';
|
|
var createNode;
|
var win = window;
|
var doc = win.document;
|
|
var vmlInited = false;
|
|
try {
|
!doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn);
|
createNode = function (tagName) {
|
return doc.createElement('<zrvml:' + tagName + ' class="zrvml">');
|
};
|
}
|
catch (e) {
|
createNode = function (tagName) {
|
return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">');
|
};
|
}
|
|
// From raphael
|
var initVML = function () {
|
if (vmlInited) {
|
return;
|
}
|
vmlInited = true;
|
|
var styleSheets = doc.styleSheets;
|
if (styleSheets.length < 31) {
|
doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)');
|
}
|
else {
|
// http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx
|
styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)');
|
}
|
};
|
|
// Not useing return to avoid error when converting to CommonJS module
|
module.exports = {
|
doc: doc,
|
initVML: initVML,
|
createNode: createNode
|
};
|
}
|
|
|
/***/ },
|
/* 419 */
|
/***/ function(module, exports, __webpack_require__) {
|
|
/**
|
* VML Painter.
|
*
|
* @module zrender/vml/Painter
|
*/
|
|
|
|
var zrLog = __webpack_require__(41);
|
var vmlCore = __webpack_require__(418);
|
|
function parseInt10(val) {
|
return parseInt(val, 10);
|
}
|
|
/**
|
* @alias module:zrender/vml/Painter
|
*/
|
function VMLPainter(root, storage) {
|
|
vmlCore.initVML();
|
|
this.root = root;
|
|
this.storage = storage;
|
|
var vmlViewport = document.createElement('div');
|
|
var vmlRoot = document.createElement('div');
|
|
vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;';
|
|
vmlRoot.style.cssText = 'position:absolute;left:0;top:0;';
|
|
root.appendChild(vmlViewport);
|
|
this._vmlRoot = vmlRoot;
|
this._vmlViewport = vmlViewport;
|
|
this.resize();
|
|
// Modify storage
|
var oldDelFromStorage = storage.delFromStorage;
|
var oldAddToStorage = storage.addToStorage;
|
storage.delFromStorage = function (el) {
|
oldDelFromStorage.call(storage, el);
|
|
if (el) {
|
el.onRemove && el.onRemove(vmlRoot);
|
}
|
};
|
|
storage.addToStorage = function (el) {
|
// Displayable already has a vml node
|
el.onAdd && el.onAdd(vmlRoot);
|
|
oldAddToStorage.call(storage, el);
|
};
|
|
this._firstPaint = true;
|
}
|
|
VMLPainter.prototype = {
|
|
constructor: VMLPainter,
|
|
/**
|
* @return {HTMLDivElement}
|
*/
|
getViewportRoot: function () {
|
return this._vmlViewport;
|
},
|
|
/**
|
* 刷新
|
*/
|
refresh: function () {
|
|
var list = this.storage.getDisplayList(true, true);
|
|
this._paintList(list);
|
},
|
|
_paintList: function (list) {
|
var vmlRoot = this._vmlRoot;
|
for (var i = 0; i < list.length; i++) {
|
var el = list[i];
|
if (el.invisible || el.ignore) {
|
if (!el.__alreadyNotVisible) {
|
el.onRemove(vmlRoot);
|
}
|
// Set as already invisible
|
el.__alreadyNotVisible = true;
|
}
|
else {
|
if (el.__alreadyNotVisible) {
|
el.onAdd(vmlRoot);
|
}
|
el.__alreadyNotVisible = false;
|
if (el.__dirty) {
|
el.beforeBrush && el.beforeBrush();
|
(el.brushVML || el.brush).call(el, vmlRoot);
|
el.afterBrush && el.afterBrush();
|
}
|
}
|
el.__dirty = false;
|
}
|
|
if (this._firstPaint) {
|
// Detached from document at first time
|
// to avoid page refreshing too many times
|
|
// FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变
|
this._vmlViewport.appendChild(vmlRoot);
|
this._firstPaint = false;
|
}
|
},
|
|
resize: function (width, height) {
|
var width = width == null ? this._getWidth() : width;
|
var height = height == null ? this._getHeight() : height;
|
|
if (this._width != width || this._height != height) {
|
this._width = width;
|
this._height = height;
|
|
var vmlViewportStyle = this._vmlViewport.style;
|
vmlViewportStyle.width = width + 'px';
|
vmlViewportStyle.height = height + 'px';
|
}
|
},
|
|
dispose: function () {
|
this.root.innerHTML = '';
|
|
this._vmlRoot =
|
this._vmlViewport =
|
this.storage = null;
|
},
|
|
getWidth: function () {
|
return this._width;
|
},
|
|
getHeight: function () {
|
return this._height;
|
},
|
|
clear: function () {
|
if (this._vmlViewport) {
|
this.root.removeChild(this._vmlViewport);
|
}
|
},
|
|
_getWidth: function () {
|
var root = this.root;
|
var stl = root.currentStyle;
|
|
return ((root.clientWidth || parseInt10(stl.width))
|
- parseInt10(stl.paddingLeft)
|
- parseInt10(stl.paddingRight)) | 0;
|
},
|
|
_getHeight: function () {
|
var root = this.root;
|
var stl = root.currentStyle;
|
|
return ((root.clientHeight || parseInt10(stl.height))
|
- parseInt10(stl.paddingTop)
|
- parseInt10(stl.paddingBottom)) | 0;
|
}
|
};
|
|
// Not supported methods
|
function createMethodNotSupport(method) {
|
return function () {
|
zrLog('In IE8.0 VML mode painter not support method "' + method + '"');
|
};
|
}
|
|
var notSupportedMethods = [
|
'getLayer', 'insertLayer', 'eachLayer', 'eachBuiltinLayer', 'eachOtherLayer', 'getLayers',
|
'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage'
|
];
|
|
for (var i = 0; i < notSupportedMethods.length; i++) {
|
var name = notSupportedMethods[i];
|
VMLPainter.prototype[name] = createMethodNotSupport(name);
|
}
|
|
module.exports = VMLPainter;
|
|
|
/***/ }
|
/******/ ])
|
});
|
;
|