瀏覽代碼

界面改版上传

yinzhipeng 4 年之前
父節點
當前提交
86969e7aeb

+ 1 - 1
app.js

@@ -3,7 +3,7 @@ let wechat = require('./utils/wechat.js');
 let log = require('./utils/log.js');
 App({
   globalData: {
-    postHeadAgreement: 'https://cdgl.pjnes.com',
+    postHeadAgreement: 'http://cdgl.pjnes.com',
     helpPhoneNum: '18211188302',
     version:'2.0'
   },

+ 2 - 1
app.json

@@ -30,7 +30,8 @@
     "pages/ucenter/notice_info/notice_info",
     "pages/ucenter/chargpilemonitor/chargpilemonitor",
     "pages/ucenter/chargpilemonitor/chargpilemonitorsearch",
-    "pages/chargemoneyresult/chargemoneyresult"
+    "pages/chargemoneyresult/chargemoneyresult",
+    "pages/ucenter/help/help"
   ],
   "window": {
     "backgroundTextStyle": "light",

+ 250 - 0
ec-canvas/ec-canvas.js

@@ -0,0 +1,250 @@
+import WxCanvas from './wx-canvas';
+import * as echarts from './echarts';
+
+let ctx;
+
+function compareVersion(v1, v2) {
+  v1 = v1.split('.')
+  v2 = v2.split('.')
+  const len = Math.max(v1.length, v2.length)
+
+  while (v1.length < len) {
+    v1.push('0')
+  }
+  while (v2.length < len) {
+    v2.push('0')
+  }
+
+  for (let i = 0; i < len; i++) {
+    const num1 = parseInt(v1[i])
+    const num2 = parseInt(v2[i])
+
+    if (num1 > num2) {
+      return 1
+    } else if (num1 < num2) {
+      return -1
+    }
+  }
+  return 0
+}
+
+Component({
+  properties: {
+    canvasId: {
+      type: String,
+      value: 'ec-canvas'
+    },
+
+    ec: {
+      type: Object
+    },
+
+    forceUseOldCanvas: {
+      type: Boolean,
+      value: false
+    }
+  },
+
+  data: {
+    isUseNewCanvas: false
+  },
+
+  ready: function () {
+    // Disable prograssive because drawImage doesn't support DOM as parameter
+    // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
+    echarts.registerPreprocessor(option => {
+      if (option && option.series) {
+        if (option.series.length > 0) {
+          option.series.forEach(series => {
+            series.progressive = 0;
+          });
+        }
+        else if (typeof option.series === 'object') {
+          option.series.progressive = 0;
+        }
+      }
+    });
+
+    if (!this.data.ec) {
+      console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
+        + 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
+      return;
+    }
+
+    if (!this.data.ec.lazyLoad) {
+      this.init();
+    }
+  },
+
+  methods: {
+    init: function (callback) {
+      const version = wx.getSystemInfoSync().SDKVersion
+
+      const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
+      const forceUseOldCanvas = this.data.forceUseOldCanvas;
+      const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
+      this.setData({ isUseNewCanvas });
+
+      if (forceUseOldCanvas && canUseNewCanvas) {
+        console.warn('开发者强制使用旧canvas,建议关闭');
+      }
+
+      if (isUseNewCanvas) {
+        // console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
+        // 2.9.0 可以使用 <canvas type="2d"></canvas>
+        this.initByNewWay(callback);
+      } else {
+        const isValid = compareVersion(version, '1.9.91') >= 0
+        if (!isValid) {
+          console.error('微信基础库版本过低,需大于等于 1.9.91。'
+            + '参见:https://github.com/ecomfe/echarts-for-weixin'
+            + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
+          return;
+        } else {
+          console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
+          this.initByOldWay(callback);
+        }
+      }
+    },
+
+    initByOldWay(callback) {
+      // 1.9.91 <= version < 2.9.0:原来的方式初始化
+      ctx = wx.createCanvasContext(this.data.canvasId, this);
+      const canvas = new WxCanvas(ctx, this.data.canvasId, false);
+
+      echarts.setCanvasCreator(() => {
+        return canvas;
+      });
+      // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
+      const canvasDpr = 1
+      var query = wx.createSelectorQuery().in(this);
+      query.select('.ec-canvas').boundingClientRect(res => {
+        if (typeof callback === 'function') {
+          this.chart = callback(canvas, res.width, res.height, canvasDpr);
+        }
+        else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+          this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
+        }
+        else {
+          this.triggerEvent('init', {
+            canvas: canvas,
+            width: res.width,
+            height: res.height,
+            canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
+          });
+        }
+      }).exec();
+    },
+
+    initByNewWay(callback) {
+      // version >= 2.9.0:使用新的方式初始化
+      const query = wx.createSelectorQuery().in(this)
+      query
+        .select('.ec-canvas')
+        .fields({ node: true, size: true })
+        .exec(res => {
+          const canvasNode = res[0].node
+          this.canvasNode = canvasNode
+
+          const canvasDpr = wx.getSystemInfoSync().pixelRatio
+          const canvasWidth = res[0].width
+          const canvasHeight = res[0].height
+
+          const ctx = canvasNode.getContext('2d')
+
+          const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
+          echarts.setCanvasCreator(() => {
+            return canvas
+          })
+
+          if (typeof callback === 'function') {
+            this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
+            this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
+          } else {
+            this.triggerEvent('init', {
+              canvas: canvas,
+              width: canvasWidth,
+              height: canvasHeight,
+              dpr: canvasDpr
+            })
+          }
+        })
+    },
+    canvasToTempFilePath(opt) {
+      if (this.data.isUseNewCanvas) {
+        // 新版
+        const query = wx.createSelectorQuery().in(this)
+        query
+          .select('.ec-canvas')
+          .fields({ node: true, size: true })
+          .exec(res => {
+            const canvasNode = res[0].node
+            opt.canvas = canvasNode
+            wx.canvasToTempFilePath(opt)
+          })
+      } else {
+        // 旧的
+        if (!opt.canvasId) {
+          opt.canvasId = this.data.canvasId;
+        }
+        ctx.draw(true, () => {
+          wx.canvasToTempFilePath(opt, this);
+        });
+      }
+    },
+
+    touchStart(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousedown', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'start');
+      }
+    },
+
+    touchMove(e) {
+      if (this.chart && e.touches.length > 0) {
+        var touch = e.touches[0];
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mousemove', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'change');
+      }
+    },
+
+    touchEnd(e) {
+      if (this.chart) {
+        const touch = e.changedTouches ? e.changedTouches[0] : {};
+        var handler = this.chart.getZr().handler;
+        handler.dispatch('mouseup', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.dispatch('click', {
+          zrX: touch.x,
+          zrY: touch.y
+        });
+        handler.processGesture(wrapTouch(e), 'end');
+      }
+    }
+  }
+});
+
+function wrapTouch(event) {
+  for (let i = 0; i < event.touches.length; ++i) {
+    const touch = event.touches[i];
+    touch.offsetX = touch.x;
+    touch.offsetY = touch.y;
+  }
+  return event;
+}

+ 4 - 0
ec-canvas/ec-canvas.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 4 - 0
ec-canvas/ec-canvas.wxml

@@ -0,0 +1,4 @@
+<!-- 新的:接口对其了H5 -->
+<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>
+<!-- 旧的 -->
+<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>

+ 4 - 0
ec-canvas/ec-canvas.wxss

@@ -0,0 +1,4 @@
+.ec-canvas {
+  width: 100%;
+  height: 100%;
+}

+ 17833 - 0
ec-canvas/echarts-liquidfill.js

@@ -0,0 +1,17833 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory(require("echarts"));
+	else if(typeof define === 'function' && define.amd)
+		define(["echarts"], factory);
+	else if(typeof exports === 'object')
+		exports["echarts-liquidfill"] = factory(require("echarts"));
+	else
+		root["echarts-liquidfill"] = factory(root["echarts"]);
+})(window, function(__WEBPACK_EXTERNAL_MODULE_echarts_lib_echarts__) {
+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] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = 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;
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// define __esModule on exports
+/******/ 	__webpack_require__.r = function(exports) {
+/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ 		}
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
+/******/ 	};
+/******/
+/******/ 	// create a fake namespace object
+/******/ 	// mode & 1: value is a module id, require it
+/******/ 	// mode & 2: merge all properties of value into the ns
+/******/ 	// mode & 4: return value when already ns object
+/******/ 	// mode & 8|1: behave like require
+/******/ 	__webpack_require__.t = function(value, mode) {
+/******/ 		if(mode & 1) value = __webpack_require__(value);
+/******/ 		if(mode & 8) return value;
+/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ 		var ns = Object.create(null);
+/******/ 		__webpack_require__.r(ns);
+/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ 		return ns;
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = "./index.js");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "./index.js":
+/*!******************!*\
+  !*** ./index.js ***!
+  \******************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = __webpack_require__(/*! ./src/liquidFill */ "./src/liquidFill.js");
+
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/config.js":
+/*!********************************************!*\
+  !*** ./node_modules/echarts/lib/config.js ***!
+  \********************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(global) {
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+// (1) The code `if (__DEV__) ...` can be removed by build tool.
+// (2) If intend to use `__DEV__`, this module should be imported. Use a global
+// variable `__DEV__` may cause that miss the declaration (see #6535), or the
+// declaration is behind of the using position (for example in `Model.extent`,
+// And tools like rollup can not analysis the dependency if not import).
+var dev; // In browser
+
+if (typeof window !== 'undefined') {
+  dev = window.__DEV__;
+} // In node
+else if (typeof global !== 'undefined') {
+    dev = global.__DEV__;
+  }
+
+if (typeof dev === 'undefined') {
+  dev = true;
+}
+
+var __DEV__ = dev;
+exports.__DEV__ = __DEV__;
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/DataDimensionInfo.js":
+/*!************************************************************!*\
+  !*** ./node_modules/echarts/lib/data/DataDimensionInfo.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var zrUtil = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/**
+ * @class
+ * @param {Object|DataDimensionInfo} [opt] All of the fields will be shallow copied.
+ */
+function DataDimensionInfo(opt) {
+  if (opt != null) {
+    zrUtil.extend(this, opt);
+  }
+  /**
+   * Dimension name.
+   * Mandatory.
+   * @type {string}
+   */
+  // this.name;
+
+  /**
+   * The origin name in dimsDef, see source helper.
+   * If displayName given, the tooltip will displayed vertically.
+   * Optional.
+   * @type {string}
+   */
+  // this.displayName;
+
+  /**
+   * Which coordSys dimension this dimension mapped to.
+   * A `coordDim` can be a "coordSysDim" that the coordSys required
+   * (for example, an item in `coordSysDims` of `model/referHelper#CoordSysInfo`),
+   * or an generated "extra coord name" if does not mapped to any "coordSysDim"
+   * (That is determined by whether `isExtraCoord` is `true`).
+   * Mandatory.
+   * @type {string}
+   */
+  // this.coordDim;
+
+  /**
+   * The index of this dimension in `series.encode[coordDim]`.
+   * Mandatory.
+   * @type {number}
+   */
+  // this.coordDimIndex;
+
+  /**
+   * Dimension type. The enumerable values are the key of
+   * `dataCtors` of `data/List`.
+   * Optional.
+   * @type {string}
+   */
+  // this.type;
+
+  /**
+   * This index of this dimension info in `data/List#_dimensionInfos`.
+   * Mandatory after added to `data/List`.
+   * @type {number}
+   */
+  // this.index;
+
+  /**
+   * The format of `otherDims` is:
+   * ```js
+   * {
+   *     tooltip: number optional,
+   *     label: number optional,
+   *     itemName: number optional,
+   *     seriesName: number optional,
+   * }
+   * ```
+   *
+   * A `series.encode` can specified these fields:
+   * ```js
+   * encode: {
+   *     // "3, 1, 5" is the index of data dimension.
+   *     tooltip: [3, 1, 5],
+   *     label: [0, 3],
+   *     ...
+   * }
+   * ```
+   * `otherDims` is the parse result of the `series.encode` above, like:
+   * ```js
+   * // Suppose the index of this data dimension is `3`.
+   * this.otherDims = {
+   *     // `3` is at the index `0` of the `encode.tooltip`
+   *     tooltip: 0,
+   *     // `3` is at the index `1` of the `encode.tooltip`
+   *     label: 1
+   * };
+   * ```
+   *
+   * This prop should never be `null`/`undefined` after initialized.
+   * @type {Object}
+   */
+
+
+  this.otherDims = {};
+  /**
+   * Be `true` if this dimension is not mapped to any "coordSysDim" that the
+   * "coordSys" required.
+   * Mandatory.
+   * @type {boolean}
+   */
+  // this.isExtraCoord;
+
+  /**
+   * @type {module:data/OrdinalMeta}
+   */
+  // this.ordinalMeta;
+
+  /**
+   * Whether to create inverted indices.
+   * @type {boolean}
+   */
+  // this.createInvertedIndices;
+}
+
+;
+var _default = DataDimensionInfo;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/Source.js":
+/*!*************************************************!*\
+  !*** ./node_modules/echarts/lib/data/Source.js ***!
+  \*************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _util = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var createHashMap = _util.createHashMap;
+var isTypedArray = _util.isTypedArray;
+
+var _clazz = __webpack_require__(/*! ../util/clazz */ "./node_modules/echarts/lib/util/clazz.js");
+
+var enableClassCheck = _clazz.enableClassCheck;
+
+var _sourceType = __webpack_require__(/*! ./helper/sourceType */ "./node_modules/echarts/lib/data/helper/sourceType.js");
+
+var SOURCE_FORMAT_ORIGINAL = _sourceType.SOURCE_FORMAT_ORIGINAL;
+var SERIES_LAYOUT_BY_COLUMN = _sourceType.SERIES_LAYOUT_BY_COLUMN;
+var SOURCE_FORMAT_UNKNOWN = _sourceType.SOURCE_FORMAT_UNKNOWN;
+var SOURCE_FORMAT_TYPED_ARRAY = _sourceType.SOURCE_FORMAT_TYPED_ARRAY;
+var SOURCE_FORMAT_KEYED_COLUMNS = _sourceType.SOURCE_FORMAT_KEYED_COLUMNS;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/**
+ * [sourceFormat]
+ *
+ * + "original":
+ * This format is only used in series.data, where
+ * itemStyle can be specified in data item.
+ *
+ * + "arrayRows":
+ * [
+ *     ['product', 'score', 'amount'],
+ *     ['Matcha Latte', 89.3, 95.8],
+ *     ['Milk Tea', 92.1, 89.4],
+ *     ['Cheese Cocoa', 94.4, 91.2],
+ *     ['Walnut Brownie', 85.4, 76.9]
+ * ]
+ *
+ * + "objectRows":
+ * [
+ *     {product: 'Matcha Latte', score: 89.3, amount: 95.8},
+ *     {product: 'Milk Tea', score: 92.1, amount: 89.4},
+ *     {product: 'Cheese Cocoa', score: 94.4, amount: 91.2},
+ *     {product: 'Walnut Brownie', score: 85.4, amount: 76.9}
+ * ]
+ *
+ * + "keyedColumns":
+ * {
+ *     'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],
+ *     'count': [823, 235, 1042, 988],
+ *     'score': [95.8, 81.4, 91.2, 76.9]
+ * }
+ *
+ * + "typedArray"
+ *
+ * + "unknown"
+ */
+
+/**
+ * @constructor
+ * @param {Object} fields
+ * @param {string} fields.sourceFormat
+ * @param {Array|Object} fields.fromDataset
+ * @param {Array|Object} [fields.data]
+ * @param {string} [seriesLayoutBy='column']
+ * @param {Array.<Object|string>} [dimensionsDefine]
+ * @param {Objet|HashMap} [encodeDefine]
+ * @param {number} [startIndex=0]
+ * @param {number} [dimensionsDetectCount]
+ */
+function Source(fields) {
+  /**
+   * @type {boolean}
+   */
+  this.fromDataset = fields.fromDataset;
+  /**
+   * Not null/undefined.
+   * @type {Array|Object}
+   */
+
+  this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []);
+  /**
+   * See also "detectSourceFormat".
+   * Not null/undefined.
+   * @type {string}
+   */
+
+  this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN;
+  /**
+   * 'row' or 'column'
+   * Not null/undefined.
+   * @type {string} seriesLayoutBy
+   */
+
+  this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN;
+  /**
+   * dimensions definition in option.
+   * can be null/undefined.
+   * @type {Array.<Object|string>}
+   */
+
+  this.dimensionsDefine = fields.dimensionsDefine;
+  /**
+   * encode definition in option.
+   * can be null/undefined.
+   * @type {Objet|HashMap}
+   */
+
+  this.encodeDefine = fields.encodeDefine && createHashMap(fields.encodeDefine);
+  /**
+   * Not null/undefined, uint.
+   * @type {number}
+   */
+
+  this.startIndex = fields.startIndex || 0;
+  /**
+   * Can be null/undefined (when unknown), uint.
+   * @type {number}
+   */
+
+  this.dimensionsDetectCount = fields.dimensionsDetectCount;
+}
+/**
+ * Wrap original series data for some compatibility cases.
+ */
+
+
+Source.seriesDataToSource = function (data) {
+  return new Source({
+    data: data,
+    sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL,
+    fromDataset: false
+  });
+};
+
+enableClassCheck(Source);
+var _default = Source;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/helper/completeDimensions.js":
+/*!********************************************************************!*\
+  !*** ./node_modules/echarts/lib/data/helper/completeDimensions.js ***!
+  \********************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _util = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var createHashMap = _util.createHashMap;
+var each = _util.each;
+var isString = _util.isString;
+var defaults = _util.defaults;
+var extend = _util.extend;
+var isObject = _util.isObject;
+var clone = _util.clone;
+
+var _model = __webpack_require__(/*! ../../util/model */ "./node_modules/echarts/lib/util/model.js");
+
+var normalizeToArray = _model.normalizeToArray;
+
+var _sourceHelper = __webpack_require__(/*! ./sourceHelper */ "./node_modules/echarts/lib/data/helper/sourceHelper.js");
+
+var guessOrdinal = _sourceHelper.guessOrdinal;
+var BE_ORDINAL = _sourceHelper.BE_ORDINAL;
+
+var Source = __webpack_require__(/*! ../Source */ "./node_modules/echarts/lib/data/Source.js");
+
+var _dimensionHelper = __webpack_require__(/*! ./dimensionHelper */ "./node_modules/echarts/lib/data/helper/dimensionHelper.js");
+
+var OTHER_DIMENSIONS = _dimensionHelper.OTHER_DIMENSIONS;
+
+var DataDimensionInfo = __webpack_require__(/*! ../DataDimensionInfo */ "./node_modules/echarts/lib/data/DataDimensionInfo.js");
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/**
+ * @deprecated
+ * Use `echarts/data/helper/createDimensions` instead.
+ */
+
+/**
+ * @see {module:echarts/test/ut/spec/data/completeDimensions}
+ *
+ * This method builds the relationship between:
+ * + "what the coord sys or series requires (see `sysDims`)",
+ * + "what the user defines (in `encode` and `dimensions`, see `opt.dimsDef` and `opt.encodeDef`)"
+ * + "what the data source provids (see `source`)".
+ *
+ * Some guess strategy will be adapted if user does not define something.
+ * If no 'value' dimension specified, the first no-named dimension will be
+ * named as 'value'.
+ *
+ * @param {Array.<string>} sysDims Necessary dimensions, like ['x', 'y'], which
+ *      provides not only dim template, but also default order.
+ *      properties: 'name', 'type', 'displayName'.
+ *      `name` of each item provides default coord name.
+ *      [{dimsDef: [string|Object, ...]}, ...] dimsDef of sysDim item provides default dim name, and
+ *                                    provide dims count that the sysDim required.
+ *      [{ordinalMeta}] can be specified.
+ * @param {module:echarts/data/Source|Array|Object} source or data (for compatibal with pervious)
+ * @param {Object} [opt]
+ * @param {Array.<Object|string>} [opt.dimsDef] option.series.dimensions User defined dimensions
+ *      For example: ['asdf', {name, type}, ...].
+ * @param {Object|HashMap} [opt.encodeDef] option.series.encode {x: 2, y: [3, 1], tooltip: [1, 2], label: 3}
+ * @param {Function} [opt.encodeDefaulter] Called if no `opt.encodeDef` exists.
+ *      If not specified, auto find the next available data dim.
+ *      param source {module:data/Source}
+ *      param dimCount {number}
+ *      return {Object} encode Never be `null/undefined`.
+ * @param {string} [opt.generateCoord] Generate coord dim with the given name.
+ *      If not specified, extra dim names will be:
+ *      'value', 'value0', 'value1', ...
+ * @param {number} [opt.generateCoordCount] By default, the generated dim name is `generateCoord`.
+ *      If `generateCoordCount` specified, the generated dim names will be:
+ *      `generateCoord` + 0, `generateCoord` + 1, ...
+ *      can be Infinity, indicate that use all of the remain columns.
+ * @param {number} [opt.dimCount] If not specified, guess by the first data item.
+ * @return {Array.<module:data/DataDimensionInfo>}
+ */
+function completeDimensions(sysDims, source, opt) {
+  if (!Source.isInstance(source)) {
+    source = Source.seriesDataToSource(source);
+  }
+
+  opt = opt || {};
+  sysDims = (sysDims || []).slice();
+  var dimsDef = (opt.dimsDef || []).slice();
+  var dataDimNameMap = createHashMap();
+  var coordDimNameMap = createHashMap(); // var valueCandidate;
+
+  var result = [];
+  var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimCount); // Apply user defined dims (`name` and `type`) and init result.
+
+  for (var i = 0; i < dimCount; i++) {
+    var dimDefItem = dimsDef[i] = extend({}, isObject(dimsDef[i]) ? dimsDef[i] : {
+      name: dimsDef[i]
+    });
+    var userDimName = dimDefItem.name;
+    var resultItem = result[i] = new DataDimensionInfo(); // Name will be applied later for avoiding duplication.
+
+    if (userDimName != null && dataDimNameMap.get(userDimName) == null) {
+      // Only if `series.dimensions` is defined in option
+      // displayName, will be set, and dimension will be diplayed vertically in
+      // tooltip by default.
+      resultItem.name = resultItem.displayName = userDimName;
+      dataDimNameMap.set(userDimName, i);
+    }
+
+    dimDefItem.type != null && (resultItem.type = dimDefItem.type);
+    dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName);
+  }
+
+  var encodeDef = opt.encodeDef;
+
+  if (!encodeDef && opt.encodeDefaulter) {
+    encodeDef = opt.encodeDefaulter(source, dimCount);
+  }
+
+  encodeDef = createHashMap(encodeDef); // Set `coordDim` and `coordDimIndex` by `encodeDef` and normalize `encodeDef`.
+
+  encodeDef.each(function (dataDims, coordDim) {
+    dataDims = normalizeToArray(dataDims).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is
+    // `{encode: {x: -1, y: 1}}`. Should not filter anything in
+    // this case.
+
+    if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) {
+      encodeDef.set(coordDim, false);
+      return;
+    }
+
+    var validDataDims = encodeDef.set(coordDim, []);
+    each(dataDims, function (resultDimIdx, idx) {
+      // The input resultDimIdx can be dim name or index.
+      isString(resultDimIdx) && (resultDimIdx = dataDimNameMap.get(resultDimIdx));
+
+      if (resultDimIdx != null && resultDimIdx < dimCount) {
+        validDataDims[idx] = resultDimIdx;
+        applyDim(result[resultDimIdx], coordDim, idx);
+      }
+    });
+  }); // Apply templetes and default order from `sysDims`.
+
+  var availDimIdx = 0;
+  each(sysDims, function (sysDimItem, sysDimIndex) {
+    var coordDim;
+    var sysDimItem;
+    var sysDimItemDimsDef;
+    var sysDimItemOtherDims;
+
+    if (isString(sysDimItem)) {
+      coordDim = sysDimItem;
+      sysDimItem = {};
+    } else {
+      coordDim = sysDimItem.name;
+      var ordinalMeta = sysDimItem.ordinalMeta;
+      sysDimItem.ordinalMeta = null;
+      sysDimItem = clone(sysDimItem);
+      sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly.
+
+      sysDimItemDimsDef = sysDimItem.dimsDef;
+      sysDimItemOtherDims = sysDimItem.otherDims;
+      sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null;
+    }
+
+    var dataDims = encodeDef.get(coordDim); // negative resultDimIdx means no need to mapping.
+
+    if (dataDims === false) {
+      return;
+    }
+
+    var dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences.
+
+    if (!dataDims.length) {
+      for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) {
+        while (availDimIdx < result.length && result[availDimIdx].coordDim != null) {
+          availDimIdx++;
+        }
+
+        availDimIdx < result.length && dataDims.push(availDimIdx++);
+      }
+    } // Apply templates.
+
+
+    each(dataDims, function (resultDimIdx, coordDimIndex) {
+      var resultItem = result[resultDimIdx];
+      applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex);
+
+      if (resultItem.name == null && sysDimItemDimsDef) {
+        var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex];
+        !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = {
+          name: sysDimItemDimsDefItem
+        });
+        resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name;
+        resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip;
+      } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}}
+
+
+      sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims);
+    });
+  });
+
+  function applyDim(resultItem, coordDim, coordDimIndex) {
+    if (OTHER_DIMENSIONS.get(coordDim) != null) {
+      resultItem.otherDims[coordDim] = coordDimIndex;
+    } else {
+      resultItem.coordDim = coordDim;
+      resultItem.coordDimIndex = coordDimIndex;
+      coordDimNameMap.set(coordDim, true);
+    }
+  } // Make sure the first extra dim is 'value'.
+
+
+  var generateCoord = opt.generateCoord;
+  var generateCoordCount = opt.generateCoordCount;
+  var fromZero = generateCoordCount != null;
+  generateCoordCount = generateCoord ? generateCoordCount || 1 : 0;
+  var extra = generateCoord || 'value'; // Set dim `name` and other `coordDim` and other props.
+
+  for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) {
+    var resultItem = result[resultDimIdx] = result[resultDimIdx] || new DataDimensionInfo();
+    var coordDim = resultItem.coordDim;
+
+    if (coordDim == null) {
+      resultItem.coordDim = genName(extra, coordDimNameMap, fromZero);
+      resultItem.coordDimIndex = 0;
+
+      if (!generateCoord || generateCoordCount <= 0) {
+        resultItem.isExtraCoord = true;
+      }
+
+      generateCoordCount--;
+    }
+
+    resultItem.name == null && (resultItem.name = genName(resultItem.coordDim, dataDimNameMap));
+
+    if (resultItem.type == null && (guessOrdinal(source, resultDimIdx, resultItem.name) === BE_ORDINAL.Must // Consider the case:
+    // {
+    //    dataset: {source: [
+    //        ['2001', 123],
+    //        ['2002', 456],
+    //        ...
+    //        ['The others', 987],
+    //    ]},
+    //    series: {type: 'pie'}
+    // }
+    // The first colum should better be treated as a "ordinal" although it
+    // might not able to be detected as an "ordinal" by `guessOrdinal`.
+    || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) {
+      resultItem.type = 'ordinal';
+    }
+  }
+
+  return result;
+} // ??? TODO
+// Originally detect dimCount by data[0]. Should we
+// optimize it to only by sysDims and dimensions and encode.
+// So only necessary dims will be initialized.
+// But
+// (1) custom series should be considered. where other dims
+// may be visited.
+// (2) sometimes user need to calcualte bubble size or use visualMap
+// on other dimensions besides coordSys needed.
+// So, dims that is not used by system, should be shared in storage?
+
+
+function getDimCount(source, sysDims, dimsDef, optDimCount) {
+  // Note that the result dimCount should not small than columns count
+  // of data, otherwise `dataDimNameMap` checking will be incorrect.
+  var dimCount = Math.max(source.dimensionsDetectCount || 1, sysDims.length, dimsDef.length, optDimCount || 0);
+  each(sysDims, function (sysDimItem) {
+    var sysDimItemDimsDef = sysDimItem.dimsDef;
+    sysDimItemDimsDef && (dimCount = Math.max(dimCount, sysDimItemDimsDef.length));
+  });
+  return dimCount;
+}
+
+function genName(name, map, fromZero) {
+  if (fromZero || map.get(name) != null) {
+    var i = 0;
+
+    while (map.get(name + i) != null) {
+      i++;
+    }
+
+    name += i;
+  }
+
+  map.set(name, true);
+  return name;
+}
+
+var _default = completeDimensions;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/helper/dimensionHelper.js":
+/*!*****************************************************************!*\
+  !*** ./node_modules/echarts/lib/data/helper/dimensionHelper.js ***!
+  \*****************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _util = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var each = _util.each;
+var createHashMap = _util.createHashMap;
+var assert = _util.assert;
+
+var _config = __webpack_require__(/*! ../../config */ "./node_modules/echarts/lib/config.js");
+
+var __DEV__ = _config.__DEV__;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+var OTHER_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'seriesName']);
+
+function summarizeDimensions(data) {
+  var summary = {};
+  var encode = summary.encode = {};
+  var notExtraCoordDimMap = createHashMap();
+  var defaultedLabel = [];
+  var defaultedTooltip = []; // See the comment of `List.js#userOutput`.
+
+  var userOutput = summary.userOutput = {
+    dimensionNames: data.dimensions.slice(),
+    encode: {}
+  };
+  each(data.dimensions, function (dimName) {
+    var dimItem = data.getDimensionInfo(dimName);
+    var coordDim = dimItem.coordDim;
+
+    if (coordDim) {
+      var coordDimIndex = dimItem.coordDimIndex;
+      getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName;
+
+      if (!dimItem.isExtraCoord) {
+        notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label,
+        // because when dataset is used, it is hard to guess which dimension
+        // can be value dimension. If both show x, y on label is not look good,
+        // and conventionally y axis is focused more.
+
+        if (mayLabelDimType(dimItem.type)) {
+          defaultedLabel[0] = dimName;
+        } // User output encode do not contain generated coords.
+        // And it only has index. User can use index to retrieve value from the raw item array.
+
+
+        getOrCreateEncodeArr(userOutput.encode, coordDim)[coordDimIndex] = dimItem.index;
+      }
+
+      if (dimItem.defaultTooltip) {
+        defaultedTooltip.push(dimName);
+      }
+    }
+
+    OTHER_DIMENSIONS.each(function (v, otherDim) {
+      var encodeArr = getOrCreateEncodeArr(encode, otherDim);
+      var dimIndex = dimItem.otherDims[otherDim];
+
+      if (dimIndex != null && dimIndex !== false) {
+        encodeArr[dimIndex] = dimItem.name;
+      }
+    });
+  });
+  var dataDimsOnCoord = [];
+  var encodeFirstDimNotExtra = {};
+  notExtraCoordDimMap.each(function (v, coordDim) {
+    var dimArr = encode[coordDim]; // ??? FIXME extra coord should not be set in dataDimsOnCoord.
+    // But should fix the case that radar axes: simplify the logic
+    // of `completeDimension`, remove `extraPrefix`.
+
+    encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data
+    // dim canot on more than one coordDim.
+
+    dataDimsOnCoord = dataDimsOnCoord.concat(dimArr);
+  });
+  summary.dataDimsOnCoord = dataDimsOnCoord;
+  summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra;
+  var encodeLabel = encode.label; // FIXME `encode.label` is not recommanded, because formatter can not be set
+  // in this way. Use label.formatter instead. May be remove this approach someday.
+
+  if (encodeLabel && encodeLabel.length) {
+    defaultedLabel = encodeLabel.slice();
+  }
+
+  var encodeTooltip = encode.tooltip;
+
+  if (encodeTooltip && encodeTooltip.length) {
+    defaultedTooltip = encodeTooltip.slice();
+  } else if (!defaultedTooltip.length) {
+    defaultedTooltip = defaultedLabel.slice();
+  }
+
+  encode.defaultedLabel = defaultedLabel;
+  encode.defaultedTooltip = defaultedTooltip;
+  return summary;
+}
+
+function getOrCreateEncodeArr(encode, dim) {
+  if (!encode.hasOwnProperty(dim)) {
+    encode[dim] = [];
+  }
+
+  return encode[dim];
+}
+
+function getDimensionTypeByAxis(axisType) {
+  return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float';
+}
+
+function mayLabelDimType(dimType) {
+  // In most cases, ordinal and time do not suitable for label.
+  // Ordinal info can be displayed on axis. Time is too long.
+  return !(dimType === 'ordinal' || dimType === 'time');
+} // function findTheLastDimMayLabel(data) {
+//     // Get last value dim
+//     var dimensions = data.dimensions.slice();
+//     var valueType;
+//     var valueDim;
+//     while (dimensions.length && (
+//         valueDim = dimensions.pop(),
+//         valueType = data.getDimensionInfo(valueDim).type,
+//         valueType === 'ordinal' || valueType === 'time'
+//     )) {} // jshint ignore:line
+//     return valueDim;
+// }
+
+
+exports.OTHER_DIMENSIONS = OTHER_DIMENSIONS;
+exports.summarizeDimensions = summarizeDimensions;
+exports.getDimensionTypeByAxis = getDimensionTypeByAxis;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/helper/sourceHelper.js":
+/*!**************************************************************!*\
+  !*** ./node_modules/echarts/lib/data/helper/sourceHelper.js ***!
+  \**************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _config = __webpack_require__(/*! ../../config */ "./node_modules/echarts/lib/config.js");
+
+var __DEV__ = _config.__DEV__;
+
+var _model = __webpack_require__(/*! ../../util/model */ "./node_modules/echarts/lib/util/model.js");
+
+var makeInner = _model.makeInner;
+var getDataItemValue = _model.getDataItemValue;
+
+var _util = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var createHashMap = _util.createHashMap;
+var each = _util.each;
+var map = _util.map;
+var isArray = _util.isArray;
+var isString = _util.isString;
+var isObject = _util.isObject;
+var isTypedArray = _util.isTypedArray;
+var isArrayLike = _util.isArrayLike;
+var extend = _util.extend;
+var assert = _util.assert;
+
+var Source = __webpack_require__(/*! ../Source */ "./node_modules/echarts/lib/data/Source.js");
+
+var _sourceType = __webpack_require__(/*! ./sourceType */ "./node_modules/echarts/lib/data/helper/sourceType.js");
+
+var SOURCE_FORMAT_ORIGINAL = _sourceType.SOURCE_FORMAT_ORIGINAL;
+var SOURCE_FORMAT_ARRAY_ROWS = _sourceType.SOURCE_FORMAT_ARRAY_ROWS;
+var SOURCE_FORMAT_OBJECT_ROWS = _sourceType.SOURCE_FORMAT_OBJECT_ROWS;
+var SOURCE_FORMAT_KEYED_COLUMNS = _sourceType.SOURCE_FORMAT_KEYED_COLUMNS;
+var SOURCE_FORMAT_UNKNOWN = _sourceType.SOURCE_FORMAT_UNKNOWN;
+var SOURCE_FORMAT_TYPED_ARRAY = _sourceType.SOURCE_FORMAT_TYPED_ARRAY;
+var SERIES_LAYOUT_BY_ROW = _sourceType.SERIES_LAYOUT_BY_ROW;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+// The result of `guessOrdinal`.
+var BE_ORDINAL = {
+  Must: 1,
+  // Encounter string but not '-' and not number-like.
+  Might: 2,
+  // Encounter string but number-like.
+  Not: 3 // Other cases
+
+};
+var inner = makeInner();
+/**
+ * @see {module:echarts/data/Source}
+ * @param {module:echarts/component/dataset/DatasetModel} datasetModel
+ * @return {string} sourceFormat
+ */
+
+function detectSourceFormat(datasetModel) {
+  var data = datasetModel.option.source;
+  var sourceFormat = SOURCE_FORMAT_UNKNOWN;
+
+  if (isTypedArray(data)) {
+    sourceFormat = SOURCE_FORMAT_TYPED_ARRAY;
+  } else if (isArray(data)) {
+    // FIXME Whether tolerate null in top level array?
+    if (data.length === 0) {
+      sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
+    }
+
+    for (var i = 0, len = data.length; i < len; i++) {
+      var item = data[i];
+
+      if (item == null) {
+        continue;
+      } else if (isArray(item)) {
+        sourceFormat = SOURCE_FORMAT_ARRAY_ROWS;
+        break;
+      } else if (isObject(item)) {
+        sourceFormat = SOURCE_FORMAT_OBJECT_ROWS;
+        break;
+      }
+    }
+  } else if (isObject(data)) {
+    for (var key in data) {
+      if (data.hasOwnProperty(key) && isArrayLike(data[key])) {
+        sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS;
+        break;
+      }
+    }
+  } else if (data != null) {
+    throw new Error('Invalid data');
+  }
+
+  inner(datasetModel).sourceFormat = sourceFormat;
+}
+/**
+ * [Scenarios]:
+ * (1) Provide source data directly:
+ *     series: {
+ *         encode: {...},
+ *         dimensions: [...]
+ *         seriesLayoutBy: 'row',
+ *         data: [[...]]
+ *     }
+ * (2) Refer to datasetModel.
+ *     series: [{
+ *         encode: {...}
+ *         // Ignore datasetIndex means `datasetIndex: 0`
+ *         // and the dimensions defination in dataset is used
+ *     }, {
+ *         encode: {...},
+ *         seriesLayoutBy: 'column',
+ *         datasetIndex: 1
+ *     }]
+ *
+ * Get data from series itself or datset.
+ * @return {module:echarts/data/Source} source
+ */
+
+
+function getSource(seriesModel) {
+  return inner(seriesModel).source;
+}
+/**
+ * MUST be called before mergeOption of all series.
+ * @param {module:echarts/model/Global} ecModel
+ */
+
+
+function resetSourceDefaulter(ecModel) {
+  // `datasetMap` is used to make default encode.
+  inner(ecModel).datasetMap = createHashMap();
+}
+/**
+ * [Caution]:
+ * MUST be called after series option merged and
+ * before "series.getInitailData()" called.
+ *
+ * [The rule of making default encode]:
+ * Category axis (if exists) alway map to the first dimension.
+ * Each other axis occupies a subsequent dimension.
+ *
+ * [Why make default encode]:
+ * Simplify the typing of encode in option, avoiding the case like that:
+ * series: [{encode: {x: 0, y: 1}}, {encode: {x: 0, y: 2}}, {encode: {x: 0, y: 3}}],
+ * where the "y" have to be manually typed as "1, 2, 3, ...".
+ *
+ * @param {module:echarts/model/Series} seriesModel
+ */
+
+
+function prepareSource(seriesModel) {
+  var seriesOption = seriesModel.option;
+  var data = seriesOption.data;
+  var sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL;
+  var fromDataset = false;
+  var seriesLayoutBy = seriesOption.seriesLayoutBy;
+  var sourceHeader = seriesOption.sourceHeader;
+  var dimensionsDefine = seriesOption.dimensions;
+  var datasetModel = getDatasetModel(seriesModel);
+
+  if (datasetModel) {
+    var datasetOption = datasetModel.option;
+    data = datasetOption.source;
+    sourceFormat = inner(datasetModel).sourceFormat;
+    fromDataset = true; // These settings from series has higher priority.
+
+    seriesLayoutBy = seriesLayoutBy || datasetOption.seriesLayoutBy;
+    sourceHeader == null && (sourceHeader = datasetOption.sourceHeader);
+    dimensionsDefine = dimensionsDefine || datasetOption.dimensions;
+  }
+
+  var completeResult = completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine);
+  inner(seriesModel).source = new Source({
+    data: data,
+    fromDataset: fromDataset,
+    seriesLayoutBy: seriesLayoutBy,
+    sourceFormat: sourceFormat,
+    dimensionsDefine: completeResult.dimensionsDefine,
+    startIndex: completeResult.startIndex,
+    dimensionsDetectCount: completeResult.dimensionsDetectCount,
+    // Note: dataset option does not have `encode`.
+    encodeDefine: seriesOption.encode
+  });
+} // return {startIndex, dimensionsDefine, dimensionsCount}
+
+
+function completeBySourceData(data, sourceFormat, seriesLayoutBy, sourceHeader, dimensionsDefine) {
+  if (!data) {
+    return {
+      dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine)
+    };
+  }
+
+  var dimensionsDetectCount;
+  var startIndex;
+
+  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
+    // Rule: Most of the first line are string: it is header.
+    // Caution: consider a line with 5 string and 1 number,
+    // it still can not be sure it is a head, because the
+    // 5 string may be 5 values of category columns.
+    if (sourceHeader === 'auto' || sourceHeader == null) {
+      arrayRowsTravelFirst(function (val) {
+        // '-' is regarded as null/undefined.
+        if (val != null && val !== '-') {
+          if (isString(val)) {
+            startIndex == null && (startIndex = 1);
+          } else {
+            startIndex = 0;
+          }
+        } // 10 is an experience number, avoid long loop.
+
+      }, seriesLayoutBy, data, 10);
+    } else {
+      startIndex = sourceHeader ? 1 : 0;
+    }
+
+    if (!dimensionsDefine && startIndex === 1) {
+      dimensionsDefine = [];
+      arrayRowsTravelFirst(function (val, index) {
+        dimensionsDefine[index] = val != null ? val : '';
+      }, seriesLayoutBy, data);
+    }
+
+    dimensionsDetectCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? data.length : data[0] ? data[0].length : null;
+  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
+    if (!dimensionsDefine) {
+      dimensionsDefine = objectRowsCollectDimensions(data);
+    }
+  } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
+    if (!dimensionsDefine) {
+      dimensionsDefine = [];
+      each(data, function (colArr, key) {
+        dimensionsDefine.push(key);
+      });
+    }
+  } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
+    var value0 = getDataItemValue(data[0]);
+    dimensionsDetectCount = isArray(value0) && value0.length || 1;
+  } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {}
+
+  return {
+    startIndex: startIndex,
+    dimensionsDefine: normalizeDimensionsDefine(dimensionsDefine),
+    dimensionsDetectCount: dimensionsDetectCount
+  };
+} // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'],
+// which is reasonable. But dimension name is duplicated.
+// Returns undefined or an array contains only object without null/undefiend or string.
+
+
+function normalizeDimensionsDefine(dimensionsDefine) {
+  if (!dimensionsDefine) {
+    // The meaning of null/undefined is different from empty array.
+    return;
+  }
+
+  var nameMap = createHashMap();
+  return map(dimensionsDefine, function (item, index) {
+    item = extend({}, isObject(item) ? item : {
+      name: item
+    }); // User can set null in dimensions.
+    // We dont auto specify name, othewise a given name may
+    // cause it be refered unexpectedly.
+
+    if (item.name == null) {
+      return item;
+    } // Also consider number form like 2012.
+
+
+    item.name += ''; // User may also specify displayName.
+    // displayName will always exists except user not
+    // specified or dim name is not specified or detected.
+    // (A auto generated dim name will not be used as
+    // displayName).
+
+    if (item.displayName == null) {
+      item.displayName = item.name;
+    }
+
+    var exist = nameMap.get(item.name);
+
+    if (!exist) {
+      nameMap.set(item.name, {
+        count: 1
+      });
+    } else {
+      item.name += '-' + exist.count++;
+    }
+
+    return item;
+  });
+}
+
+function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) {
+  maxLoop == null && (maxLoop = Infinity);
+
+  if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
+    for (var i = 0; i < data.length && i < maxLoop; i++) {
+      cb(data[i] ? data[i][0] : null, i);
+    }
+  } else {
+    var value0 = data[0] || [];
+
+    for (var i = 0; i < value0.length && i < maxLoop; i++) {
+      cb(value0[i], i);
+    }
+  }
+}
+
+function objectRowsCollectDimensions(data) {
+  var firstIndex = 0;
+  var obj;
+
+  while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line
+
+
+  if (obj) {
+    var dimensions = [];
+    each(obj, function (value, key) {
+      dimensions.push(key);
+    });
+    return dimensions;
+  }
+}
+/**
+ * [The strategy of the arrengment of data dimensions for dataset]:
+ * "value way": all axes are non-category axes. So series one by one take
+ *     several (the number is coordSysDims.length) dimensions from dataset.
+ *     The result of data arrengment of data dimensions like:
+ *     | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y |
+ * "category way": at least one axis is category axis. So the the first data
+ *     dimension is always mapped to the first category axis and shared by
+ *     all of the series. The other data dimensions are taken by series like
+ *     "value way" does.
+ *     The result of data arrengment of data dimensions like:
+ *     | ser_shared_x | ser0_y | ser1_y | ser2_y |
+ *
+ * @param {Array.<Object|string>} coordDimensions [{name: <string>, type: <string>, dimsDef: <Array>}, ...]
+ * @param {module:model/Series} seriesModel
+ * @param {module:data/Source} source
+ * @return {Object} encode Never be `null/undefined`.
+ */
+
+
+function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) {
+  var encode = {};
+  var datasetModel = getDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.
+
+  if (!datasetModel || !coordDimensions) {
+    return encode;
+  }
+
+  var encodeItemName = [];
+  var encodeSeriesName = [];
+  var ecModel = seriesModel.ecModel;
+  var datasetMap = inner(ecModel).datasetMap;
+  var key = datasetModel.uid + '_' + source.seriesLayoutBy;
+  var baseCategoryDimIndex;
+  var categoryWayValueDimStart;
+  coordDimensions = coordDimensions.slice();
+  each(coordDimensions, function (coordDimInfo, coordDimIdx) {
+    !isObject(coordDimInfo) && (coordDimensions[coordDimIdx] = {
+      name: coordDimInfo
+    });
+
+    if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) {
+      baseCategoryDimIndex = coordDimIdx;
+      categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimensions[coordDimIdx]);
+    }
+
+    encode[coordDimInfo.name] = [];
+  });
+  var datasetRecord = datasetMap.get(key) || datasetMap.set(key, {
+    categoryWayDim: categoryWayValueDimStart,
+    valueWayDim: 0
+  }); // TODO
+  // Auto detect first time axis and do arrangement.
+
+  each(coordDimensions, function (coordDimInfo, coordDimIdx) {
+    var coordDimName = coordDimInfo.name;
+    var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way.
+
+    if (baseCategoryDimIndex == null) {
+      var start = datasetRecord.valueWayDim;
+      pushDim(encode[coordDimName], start, count);
+      pushDim(encodeSeriesName, start, count);
+      datasetRecord.valueWayDim += count; // ??? TODO give a better default series name rule?
+      // especially when encode x y specified.
+      // consider: when mutiple series share one dimension
+      // category axis, series name should better use
+      // the other dimsion name. On the other hand, use
+      // both dimensions name.
+    } // In category way, the first category axis.
+    else if (baseCategoryDimIndex === coordDimIdx) {
+        pushDim(encode[coordDimName], 0, count);
+        pushDim(encodeItemName, 0, count);
+      } // In category way, the other axis.
+      else {
+          var start = datasetRecord.categoryWayDim;
+          pushDim(encode[coordDimName], start, count);
+          pushDim(encodeSeriesName, start, count);
+          datasetRecord.categoryWayDim += count;
+        }
+  });
+
+  function pushDim(dimIdxArr, idxFrom, idxCount) {
+    for (var i = 0; i < idxCount; i++) {
+      dimIdxArr.push(idxFrom + i);
+    }
+  }
+
+  function getDataDimCountOnCoordDim(coordDimInfo) {
+    var dimsDef = coordDimInfo.dimsDef;
+    return dimsDef ? dimsDef.length : 1;
+  }
+
+  encodeItemName.length && (encode.itemName = encodeItemName);
+  encodeSeriesName.length && (encode.seriesName = encodeSeriesName);
+  return encode;
+}
+/**
+ * Work for data like [{name: ..., value: ...}, ...].
+ *
+ * @param {module:model/Series} seriesModel
+ * @param {module:data/Source} source
+ * @return {Object} encode Never be `null/undefined`.
+ */
+
+
+function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) {
+  var encode = {};
+  var datasetModel = getDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur.
+
+  if (!datasetModel) {
+    return encode;
+  }
+
+  var sourceFormat = source.sourceFormat;
+  var dimensionsDefine = source.dimensionsDefine;
+  var potentialNameDimIndex;
+
+  if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
+    each(dimensionsDefine, function (dim, idx) {
+      if ((isObject(dim) ? dim.name : dim) === 'name') {
+        potentialNameDimIndex = idx;
+      }
+    });
+  } // idxResult: {v, n}.
+
+
+  var idxResult = function () {
+    var idxRes0 = {};
+    var idxRes1 = {};
+    var guessRecords = []; // 5 is an experience value.
+
+    for (var i = 0, len = Math.min(5, dimCount); i < len; i++) {
+      var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i);
+      guessRecords.push(guessResult);
+      var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim,
+      // and then find a name dim with the priority:
+      // "BE_ORDINAL.Might|BE_ORDINAL.Must" > "other dim" > "the value dim itself".
+
+      if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) {
+        idxRes0.v = i;
+      }
+
+      if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) {
+        idxRes0.n = i;
+      }
+
+      if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) {
+        return idxRes0;
+      } // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),
+      // find the first BE_ORDINAL.Might as the value dim,
+      // and then find a name dim with the priority:
+      // "other dim" > "the value dim itself".
+      // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be
+      // treated as number.
+
+
+      if (!isPureNumber) {
+        if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) {
+          idxRes1.v = i;
+        }
+
+        if (idxRes1.n == null || idxRes1.n === idxRes1.v) {
+          idxRes1.n = i;
+        }
+      }
+    }
+
+    function fulfilled(idxResult) {
+      return idxResult.v != null && idxResult.n != null;
+    }
+
+    return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null;
+  }();
+
+  if (idxResult) {
+    encode.value = idxResult.v; // `potentialNameDimIndex` has highest priority.
+
+    var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label use itemName in charts.
+    // So we dont set encodeLabel here.
+
+    encode.itemName = [nameDimIndex];
+    encode.seriesName = [nameDimIndex];
+  }
+
+  return encode;
+}
+/**
+ * If return null/undefined, indicate that should not use datasetModel.
+ */
+
+
+function getDatasetModel(seriesModel) {
+  var option = seriesModel.option; // Caution: consider the scenario:
+  // A dataset is declared and a series is not expected to use the dataset,
+  // and at the beginning `setOption({series: { noData })` (just prepare other
+  // option but no data), then `setOption({series: {data: [...]}); In this case,
+  // the user should set an empty array to avoid that dataset is used by default.
+
+  var thisData = option.data;
+
+  if (!thisData) {
+    return seriesModel.ecModel.getComponent('dataset', option.datasetIndex || 0);
+  }
+}
+/**
+ * The rule should not be complex, otherwise user might not
+ * be able to known where the data is wrong.
+ * The code is ugly, but how to make it neat?
+ *
+ * @param {module:echars/data/Source} source
+ * @param {number} dimIndex
+ * @return {BE_ORDINAL} guess result.
+ */
+
+
+function guessOrdinal(source, dimIndex) {
+  return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex);
+} // dimIndex may be overflow source data.
+// return {BE_ORDINAL}
+
+
+function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) {
+  var result; // Experience value.
+
+  var maxLoop = 5;
+
+  if (isTypedArray(data)) {
+    return BE_ORDINAL.Not;
+  } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine
+  // always exists in source.
+
+
+  var dimName;
+  var dimType;
+
+  if (dimensionsDefine) {
+    var dimDefItem = dimensionsDefine[dimIndex];
+
+    if (isObject(dimDefItem)) {
+      dimName = dimDefItem.name;
+      dimType = dimDefItem.type;
+    } else if (isString(dimDefItem)) {
+      dimName = dimDefItem;
+    }
+  }
+
+  if (dimType != null) {
+    return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not;
+  }
+
+  if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
+    if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) {
+      var sample = data[dimIndex];
+
+      for (var i = 0; i < (sample || []).length && i < maxLoop; i++) {
+        if ((result = detectValue(sample[startIndex + i])) != null) {
+          return result;
+        }
+      }
+    } else {
+      for (var i = 0; i < data.length && i < maxLoop; i++) {
+        var row = data[startIndex + i];
+
+        if (row && (result = detectValue(row[dimIndex])) != null) {
+          return result;
+        }
+      }
+    }
+  } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) {
+    if (!dimName) {
+      return BE_ORDINAL.Not;
+    }
+
+    for (var i = 0; i < data.length && i < maxLoop; i++) {
+      var item = data[i];
+
+      if (item && (result = detectValue(item[dimName])) != null) {
+        return result;
+      }
+    }
+  } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) {
+    if (!dimName) {
+      return BE_ORDINAL.Not;
+    }
+
+    var sample = data[dimName];
+
+    if (!sample || isTypedArray(sample)) {
+      return BE_ORDINAL.Not;
+    }
+
+    for (var i = 0; i < sample.length && i < maxLoop; i++) {
+      if ((result = detectValue(sample[i])) != null) {
+        return result;
+      }
+    }
+  } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) {
+    for (var i = 0; i < data.length && i < maxLoop; i++) {
+      var item = data[i];
+      var val = getDataItemValue(item);
+
+      if (!isArray(val)) {
+        return BE_ORDINAL.Not;
+      }
+
+      if ((result = detectValue(val[dimIndex])) != null) {
+        return result;
+      }
+    }
+  }
+
+  function detectValue(val) {
+    var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as "number".
+    // `isFinit('')` get `true`.
+
+    if (val != null && isFinite(val) && val !== '') {
+      return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not;
+    } else if (beStr && val !== '-') {
+      return BE_ORDINAL.Must;
+    }
+  }
+
+  return BE_ORDINAL.Not;
+}
+
+exports.BE_ORDINAL = BE_ORDINAL;
+exports.detectSourceFormat = detectSourceFormat;
+exports.getSource = getSource;
+exports.resetSourceDefaulter = resetSourceDefaulter;
+exports.prepareSource = prepareSource;
+exports.makeSeriesEncodeForAxisCoordSys = makeSeriesEncodeForAxisCoordSys;
+exports.makeSeriesEncodeForNameBased = makeSeriesEncodeForNameBased;
+exports.guessOrdinal = guessOrdinal;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/data/helper/sourceType.js":
+/*!************************************************************!*\
+  !*** ./node_modules/echarts/lib/data/helper/sourceType.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+// Avoid typo.
+var SOURCE_FORMAT_ORIGINAL = 'original';
+var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows';
+var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows';
+var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns';
+var SOURCE_FORMAT_UNKNOWN = 'unknown'; // ??? CHANGE A NAME
+
+var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray';
+var SERIES_LAYOUT_BY_COLUMN = 'column';
+var SERIES_LAYOUT_BY_ROW = 'row';
+exports.SOURCE_FORMAT_ORIGINAL = SOURCE_FORMAT_ORIGINAL;
+exports.SOURCE_FORMAT_ARRAY_ROWS = SOURCE_FORMAT_ARRAY_ROWS;
+exports.SOURCE_FORMAT_OBJECT_ROWS = SOURCE_FORMAT_OBJECT_ROWS;
+exports.SOURCE_FORMAT_KEYED_COLUMNS = SOURCE_FORMAT_KEYED_COLUMNS;
+exports.SOURCE_FORMAT_UNKNOWN = SOURCE_FORMAT_UNKNOWN;
+exports.SOURCE_FORMAT_TYPED_ARRAY = SOURCE_FORMAT_TYPED_ARRAY;
+exports.SERIES_LAYOUT_BY_COLUMN = SERIES_LAYOUT_BY_COLUMN;
+exports.SERIES_LAYOUT_BY_ROW = SERIES_LAYOUT_BY_ROW;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/util/clazz.js":
+/*!************************************************!*\
+  !*** ./node_modules/echarts/lib/util/clazz.js ***!
+  \************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _config = __webpack_require__(/*! ../config */ "./node_modules/echarts/lib/config.js");
+
+var __DEV__ = _config.__DEV__;
+
+var zrUtil = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+var TYPE_DELIMITER = '.';
+var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___';
+/**
+ * Notice, parseClassType('') should returns {main: '', sub: ''}
+ * @public
+ */
+
+function parseClassType(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
+ */
+
+
+function enableClassExtend(RootClass, mandatoryMethods) {
+  RootClass.$constructor = RootClass;
+
+  RootClass.extend = function (proto) {
+    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;
+  };
+}
+
+var classBase = 0;
+/**
+ * Can not use instanceof, consider different scope by
+ * cross domain or es module import in ec extensions.
+ * Mount a method "isInstance()" to Clz.
+ */
+
+function enableClassCheck(Clz) {
+  var classAttr = ['__\0is_clz', classBase++, Math.random().toFixed(3)].join('_');
+  Clz.prototype[classAttr] = true;
+
+  Clz.isInstance = function (obj) {
+    return !!(obj && obj[classAttr]);
+  };
+} // 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
+ */
+
+
+function enableClassManagement(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) {
+        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
+ */
+
+
+function setReadOnly(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]);
+  // });
+}
+
+exports.parseClassType = parseClassType;
+exports.enableClassExtend = enableClassExtend;
+exports.enableClassCheck = enableClassCheck;
+exports.enableClassManagement = enableClassManagement;
+exports.setReadOnly = setReadOnly;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/util/graphic.js":
+/*!**************************************************!*\
+  !*** ./node_modules/echarts/lib/util/graphic.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var zrUtil = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var pathTool = __webpack_require__(/*! zrender/lib/tool/path */ "./node_modules/zrender/lib/tool/path.js");
+
+var colorTool = __webpack_require__(/*! zrender/lib/tool/color */ "./node_modules/zrender/lib/tool/color.js");
+
+var matrix = __webpack_require__(/*! zrender/lib/core/matrix */ "./node_modules/zrender/lib/core/matrix.js");
+
+var vector = __webpack_require__(/*! zrender/lib/core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var Path = __webpack_require__(/*! zrender/lib/graphic/Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var Transformable = __webpack_require__(/*! zrender/lib/mixin/Transformable */ "./node_modules/zrender/lib/mixin/Transformable.js");
+
+var ZImage = __webpack_require__(/*! zrender/lib/graphic/Image */ "./node_modules/zrender/lib/graphic/Image.js");
+
+exports.Image = ZImage;
+
+var Group = __webpack_require__(/*! zrender/lib/container/Group */ "./node_modules/zrender/lib/container/Group.js");
+
+exports.Group = Group;
+
+var Text = __webpack_require__(/*! zrender/lib/graphic/Text */ "./node_modules/zrender/lib/graphic/Text.js");
+
+exports.Text = Text;
+
+var Circle = __webpack_require__(/*! zrender/lib/graphic/shape/Circle */ "./node_modules/zrender/lib/graphic/shape/Circle.js");
+
+exports.Circle = Circle;
+
+var Sector = __webpack_require__(/*! zrender/lib/graphic/shape/Sector */ "./node_modules/zrender/lib/graphic/shape/Sector.js");
+
+exports.Sector = Sector;
+
+var Ring = __webpack_require__(/*! zrender/lib/graphic/shape/Ring */ "./node_modules/zrender/lib/graphic/shape/Ring.js");
+
+exports.Ring = Ring;
+
+var Polygon = __webpack_require__(/*! zrender/lib/graphic/shape/Polygon */ "./node_modules/zrender/lib/graphic/shape/Polygon.js");
+
+exports.Polygon = Polygon;
+
+var Polyline = __webpack_require__(/*! zrender/lib/graphic/shape/Polyline */ "./node_modules/zrender/lib/graphic/shape/Polyline.js");
+
+exports.Polyline = Polyline;
+
+var Rect = __webpack_require__(/*! zrender/lib/graphic/shape/Rect */ "./node_modules/zrender/lib/graphic/shape/Rect.js");
+
+exports.Rect = Rect;
+
+var Line = __webpack_require__(/*! zrender/lib/graphic/shape/Line */ "./node_modules/zrender/lib/graphic/shape/Line.js");
+
+exports.Line = Line;
+
+var BezierCurve = __webpack_require__(/*! zrender/lib/graphic/shape/BezierCurve */ "./node_modules/zrender/lib/graphic/shape/BezierCurve.js");
+
+exports.BezierCurve = BezierCurve;
+
+var Arc = __webpack_require__(/*! zrender/lib/graphic/shape/Arc */ "./node_modules/zrender/lib/graphic/shape/Arc.js");
+
+exports.Arc = Arc;
+
+var CompoundPath = __webpack_require__(/*! zrender/lib/graphic/CompoundPath */ "./node_modules/zrender/lib/graphic/CompoundPath.js");
+
+exports.CompoundPath = CompoundPath;
+
+var LinearGradient = __webpack_require__(/*! zrender/lib/graphic/LinearGradient */ "./node_modules/zrender/lib/graphic/LinearGradient.js");
+
+exports.LinearGradient = LinearGradient;
+
+var RadialGradient = __webpack_require__(/*! zrender/lib/graphic/RadialGradient */ "./node_modules/zrender/lib/graphic/RadialGradient.js");
+
+exports.RadialGradient = RadialGradient;
+
+var BoundingRect = __webpack_require__(/*! zrender/lib/core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+exports.BoundingRect = BoundingRect;
+
+var IncrementalDisplayable = __webpack_require__(/*! zrender/lib/graphic/IncrementalDisplayable */ "./node_modules/zrender/lib/graphic/IncrementalDisplayable.js");
+
+exports.IncrementalDisplayable = IncrementalDisplayable;
+
+var subPixelOptimizeUtil = __webpack_require__(/*! zrender/lib/graphic/helper/subPixelOptimize */ "./node_modules/zrender/lib/graphic/helper/subPixelOptimize.js");
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+var mathMax = Math.max;
+var mathMin = Math.min;
+var EMPTY_OBJ = {};
+var Z2_EMPHASIS_LIFT = 1; // key: label model property nane, value: style property name.
+
+var CACHED_LABEL_STYLE_PROPERTIES = {
+  color: 'textFill',
+  textBorderColor: 'textStroke',
+  textBorderWidth: 'textStrokeWidth'
+};
+var EMPHASIS = 'emphasis';
+var NORMAL = 'normal'; // Reserve 0 as default.
+
+var _highlightNextDigit = 1;
+var _highlightKeyMap = {};
+var _customShapeMap = {};
+/**
+ * Extend shape with parameters
+ */
+
+function extendShape(opts) {
+  return Path.extend(opts);
+}
+/**
+ * Extend path
+ */
+
+
+function extendPath(pathData, opts) {
+  return pathTool.extendFromString(pathData, opts);
+}
+/**
+ * Register a user defined shape.
+ * The shape class can be fetched by `getShapeClass`
+ * This method will overwrite the registered shapes, including
+ * the registered built-in shapes, if using the same `name`.
+ * The shape can be used in `custom series` and
+ * `graphic component` by declaring `{type: name}`.
+ *
+ * @param {string} name
+ * @param {Object} ShapeClass Can be generated by `extendShape`.
+ */
+
+
+function registerShape(name, ShapeClass) {
+  _customShapeMap[name] = ShapeClass;
+}
+/**
+ * Find shape class registered by `registerShape`. Usually used in
+ * fetching user defined shape.
+ *
+ * [Caution]:
+ * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared
+ * to use user registered shapes.
+ * Because the built-in shape (see `getBuiltInShape`) will be registered by
+ * `registerShape` by default. That enables users to get both built-in
+ * shapes as well as the shapes belonging to themsleves. But users can overwrite
+ * the built-in shapes by using names like 'circle', 'rect' via calling
+ * `registerShape`. So the echarts inner featrues should not fetch shapes from here
+ * in case that it is overwritten by users, except that some features, like
+ * `custom series`, `graphic component`, do it deliberately.
+ *
+ * (2) In the features like `custom series`, `graphic component`, the user input
+ * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic
+ * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names
+ * are reserved names, that is, if some user register a shape named `'image'`,
+ * the shape will not be used. If we intending to add some more reserved names
+ * in feature, that might bring break changes (disable some existing user shape
+ * names). But that case probably rearly happen. So we dont make more mechanism
+ * to resolve this issue here.
+ *
+ * @param {string} name
+ * @return {Object} The shape class. If not found, return nothing.
+ */
+
+
+function getShapeClass(name) {
+  if (_customShapeMap.hasOwnProperty(name)) {
+    return _customShapeMap[name];
+  }
+}
+/**
+ * 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'
+ */
+
+
+function makePath(pathData, opts, rect, layout) {
+  var path = pathTool.createFromString(pathData, opts);
+
+  if (rect) {
+    if (layout === 'center') {
+      rect = centerGraphic(rect, path.getBoundingRect());
+    }
+
+    resizePath(path, rect);
+  }
+
+  return path;
+}
+/**
+ * Create a image element from image url
+ * @param {string} imageUrl image url
+ * @param {Object} opts options
+ * @param {module:zrender/core/BoundingRect} rect constrain rect
+ * @param {string} [layout=cover] 'center' or 'cover'
+ */
+
+
+function makeImage(imageUrl, rect, layout) {
+  var path = new ZImage({
+    style: {
+      image: imageUrl,
+      x: rect.x,
+      y: rect.y,
+      width: rect.width,
+      height: rect.height
+    },
+    onload: function (img) {
+      if (layout === 'center') {
+        var boundingRect = {
+          width: img.width,
+          height: img.height
+        };
+        path.setStyle(centerGraphic(rect, boundingRect));
+      }
+    }
+  });
+  return path;
+}
+/**
+ * Get position of centered element in bounding box.
+ *
+ * @param  {Object} rect         element local bounding box
+ * @param  {Object} boundingRect constraint bounding box
+ * @return {Object} element position containing x, y, width, and height
+ */
+
+
+function centerGraphic(rect, boundingRect) {
+  // Set rect to center, keep width / height ratio.
+  var aspect = boundingRect.width / boundingRect.height;
+  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;
+  return {
+    x: cx - width / 2,
+    y: cy - height / 2,
+    width: width,
+    height: height
+  };
+}
+
+var mergePath = pathTool.mergePath;
+/**
+ * Resize a path to fit the rect
+ * @param {module:zrender/graphic/Path} path
+ * @param {Object} rect
+ */
+
+function resizePath(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
+ */
+
+
+function subPixelOptimizeLine(param) {
+  subPixelOptimizeUtil.subPixelOptimizeLine(param.shape, param.shape, param.style);
+  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
+ */
+
+
+function subPixelOptimizeRect(param) {
+  subPixelOptimizeUtil.subPixelOptimizeRect(param.shape, param.shape, param.style);
+  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.
+ */
+
+
+var subPixelOptimize = subPixelOptimizeUtil.subPixelOptimize;
+
+function hasFillOrStroke(fillOrStroke) {
+  return fillOrStroke != null && fillOrStroke !== 'none';
+} // Most lifted color are duplicated.
+
+
+var liftedColorMap = zrUtil.createHashMap();
+var liftedColorCount = 0;
+
+function liftColor(color) {
+  if (typeof color !== 'string') {
+    return color;
+  }
+
+  var liftedColor = liftedColorMap.get(color);
+
+  if (!liftedColor) {
+    liftedColor = colorTool.lift(color, -0.1);
+
+    if (liftedColorCount < 10000) {
+      liftedColorMap.set(color, liftedColor);
+      liftedColorCount++;
+    }
+  }
+
+  return liftedColor;
+}
+
+function cacheElementStl(el) {
+  if (!el.__hoverStlDirty) {
+    return;
+  }
+
+  el.__hoverStlDirty = false;
+  var hoverStyle = el.__hoverStl;
+
+  if (!hoverStyle) {
+    el.__cachedNormalStl = el.__cachedNormalZ2 = null;
+    return;
+  }
+
+  var normalStyle = el.__cachedNormalStl = {};
+  el.__cachedNormalZ2 = el.z2;
+  var elStyle = el.style;
+
+  for (var name in hoverStyle) {
+    // See comment in `singleEnterEmphasis`.
+    if (hoverStyle[name] != null) {
+      normalStyle[name] = elStyle[name];
+    }
+  } // Always cache fill and stroke to normalStyle for lifting color.
+
+
+  normalStyle.fill = elStyle.fill;
+  normalStyle.stroke = elStyle.stroke;
+}
+
+function singleEnterEmphasis(el) {
+  var hoverStl = el.__hoverStl;
+
+  if (!hoverStl || el.__highlighted) {
+    return;
+  }
+
+  var zr = el.__zr;
+  var useHoverLayer = el.useHoverLayer && zr && zr.painter.type === 'canvas';
+  el.__highlighted = useHoverLayer ? 'layer' : 'plain';
+
+  if (el.isGroup || !zr && el.useHoverLayer) {
+    return;
+  }
+
+  var elTarget = el;
+  var targetStyle = el.style;
+
+  if (useHoverLayer) {
+    elTarget = zr.addHover(el);
+    targetStyle = elTarget.style;
+  }
+
+  rollbackDefaultTextStyle(targetStyle);
+
+  if (!useHoverLayer) {
+    cacheElementStl(elTarget);
+  } // styles can be:
+  // {
+  //    label: {
+  //        show: false,
+  //        position: 'outside',
+  //        fontSize: 18
+  //    },
+  //    emphasis: {
+  //        label: {
+  //            show: true
+  //        }
+  //    }
+  // },
+  // where properties of `emphasis` may not appear in `normal`. We previously use
+  // module:echarts/util/model#defaultEmphasis to merge `normal` to `emphasis`.
+  // But consider rich text and setOption in merge mode, it is impossible to cover
+  // all properties in merge. So we use merge mode when setting style here.
+  // But we choose the merge strategy that only properties that is not `null/undefined`.
+  // Because when making a textStyle (espacially rich text), it is not easy to distinguish
+  // `hasOwnProperty` and `null/undefined` in code, so we trade them as the same for simplicity.
+  // But this strategy brings a trouble that `null/undefined` can not be used to remove
+  // style any more in `emphasis`. Users can both set properties directly on normal and
+  // emphasis to avoid this issue, or we might support `'none'` for this case if required.
+
+
+  targetStyle.extendFrom(hoverStl);
+  setDefaultHoverFillStroke(targetStyle, hoverStl, 'fill');
+  setDefaultHoverFillStroke(targetStyle, hoverStl, 'stroke');
+  applyDefaultTextStyle(targetStyle);
+
+  if (!useHoverLayer) {
+    el.dirty(false);
+    el.z2 += Z2_EMPHASIS_LIFT;
+  }
+}
+
+function setDefaultHoverFillStroke(targetStyle, hoverStyle, prop) {
+  if (!hasFillOrStroke(hoverStyle[prop]) && hasFillOrStroke(targetStyle[prop])) {
+    targetStyle[prop] = liftColor(targetStyle[prop]);
+  }
+}
+
+function singleEnterNormal(el) {
+  var highlighted = el.__highlighted;
+
+  if (!highlighted) {
+    return;
+  }
+
+  el.__highlighted = false;
+
+  if (el.isGroup) {
+    return;
+  }
+
+  if (highlighted === 'layer') {
+    el.__zr && el.__zr.removeHover(el);
+  } else {
+    var style = el.style;
+    var normalStl = el.__cachedNormalStl;
+
+    if (normalStl) {
+      rollbackDefaultTextStyle(style);
+      el.setStyle(normalStl);
+      applyDefaultTextStyle(style);
+    } // `__cachedNormalZ2` will not be reset if calling `setElementHoverStyle`
+    // when `el` is on emphasis state. So here by comparing with 1, we try
+    // hard to make the bug case rare.
+
+
+    var normalZ2 = el.__cachedNormalZ2;
+
+    if (normalZ2 != null && el.z2 - normalZ2 === Z2_EMPHASIS_LIFT) {
+      el.z2 = normalZ2;
+    }
+  }
+}
+
+function traverseUpdate(el, updater, commonParam) {
+  // If root is group, also enter updater for `highDownOnUpdate`.
+  var fromState = NORMAL;
+  var toState = NORMAL;
+  var trigger; // See the rule of `highDownOnUpdate` on `graphic.setAsHighDownDispatcher`.
+
+  el.__highlighted && (fromState = EMPHASIS, trigger = true);
+  updater(el, commonParam);
+  el.__highlighted && (toState = EMPHASIS, trigger = true);
+  el.isGroup && el.traverse(function (child) {
+    !child.isGroup && updater(child, commonParam);
+  });
+  trigger && el.__highDownOnUpdate && el.__highDownOnUpdate(fromState, toState);
+}
+/**
+ * Set hover style (namely "emphasis style") of element, based on the current
+ * style of the given `el`.
+ * This method should be called after all of the normal styles have been adopted
+ * to the `el`. See the reason on `setHoverStyle`.
+ *
+ * @param {module:zrender/Element} el Should not be `zrender/container/Group`.
+ * @param {Object} [el.hoverStyle] Can be set on el or its descendants,
+ *        e.g., `el.hoverStyle = ...; graphic.setHoverStyle(el); `.
+ *        Often used when item group has a label element and it's hoverStyle is different.
+ * @param {Object|boolean} [hoverStl] The specified hover style.
+ *        If set as `false`, disable the hover style.
+ *        Similarly, The `el.hoverStyle` can alse be set
+ *        as `false` to disable the hover style.
+ *        Otherwise, use the default hover style if not provided.
+ */
+
+
+function setElementHoverStyle(el, hoverStl) {
+  // For performance consideration, it might be better to make the "hover style" only the
+  // difference properties from the "normal style", but not a entire copy of all styles.
+  hoverStl = el.__hoverStl = hoverStl !== false && (el.hoverStyle || hoverStl || {});
+  el.__hoverStlDirty = true; // FIXME
+  // It is not completely right to save "normal"/"emphasis" flag on elements.
+  // It probably should be saved on `data` of series. Consider the cases:
+  // (1) A highlighted elements are moved out of the view port and re-enter
+  // again by dataZoom.
+  // (2) call `setOption` and replace elements totally when they are highlighted.
+
+  if (el.__highlighted) {
+    // Consider the case:
+    // The styles of a highlighted `el` is being updated. The new "emphasis style"
+    // should be adapted to the `el`. Notice here new "normal styles" should have
+    // been set outside and the cached "normal style" is out of date.
+    el.__cachedNormalStl = null; // Do not clear `__cachedNormalZ2` here, because setting `z2` is not a constraint
+    // of this method. In most cases, `z2` is not set and hover style should be able
+    // to rollback. Of course, that would bring bug, but only in a rare case, see
+    // `doSingleLeaveHover` for details.
+
+    singleEnterNormal(el);
+    singleEnterEmphasis(el);
+  }
+}
+
+function onElementMouseOver(e) {
+  !shouldSilent(this, e) // "emphasis" event highlight has higher priority than mouse highlight.
+  && !this.__highByOuter && traverseUpdate(this, singleEnterEmphasis);
+}
+
+function onElementMouseOut(e) {
+  !shouldSilent(this, e) // "emphasis" event highlight has higher priority than mouse highlight.
+  && !this.__highByOuter && traverseUpdate(this, singleEnterNormal);
+}
+
+function onElementEmphasisEvent(highlightDigit) {
+  this.__highByOuter |= 1 << (highlightDigit || 0);
+  traverseUpdate(this, singleEnterEmphasis);
+}
+
+function onElementNormalEvent(highlightDigit) {
+  !(this.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdate(this, singleEnterNormal);
+}
+
+function shouldSilent(el, e) {
+  return el.__highDownSilentOnTouch && e.zrByTouch;
+}
+/**
+ * Set hover style (namely "emphasis style") of element,
+ * based on the current style of the given `el`.
+ *
+ * (1)
+ * **CONSTRAINTS** for this method:
+ * <A> This method MUST be called after all of the normal styles having been adopted
+ * to the `el`.
+ * <B> The input `hoverStyle` (that is, "emphasis style") MUST be the subset of the
+ * "normal style" having been set to the el.
+ * <C> `color` MUST be one of the "normal styles" (because color might be lifted as
+ * a default hover style).
+ *
+ * The reason: this method treat the current style of the `el` as the "normal style"
+ * and cache them when enter/update the "emphasis style". Consider the case: the `el`
+ * is in "emphasis" state and `setOption`/`dispatchAction` trigger the style updating
+ * logic, where the el should shift from the original emphasis style to the new
+ * "emphasis style" and should be able to "downplay" back to the new "normal style".
+ *
+ * Indeed, it is error-prone to make a interface has so many constraints, but I have
+ * not found a better solution yet to fit the backward compatibility, performance and
+ * the current programming style.
+ *
+ * (2)
+ * Call the method for a "root" element once. Do not call it for each descendants.
+ * If the descendants elemenets of a group has itself hover style different from the
+ * root group, we can simply mount the style on `el.hoverStyle` for them, but should
+ * not call this method for them.
+ *
+ * (3) These input parameters can be set directly on `el`:
+ *
+ * @param {module:zrender/Element} el
+ * @param {Object} [el.hoverStyle] See `graphic.setElementHoverStyle`.
+ * @param {boolean} [el.highDownSilentOnTouch=false] See `graphic.setAsHighDownDispatcher`.
+ * @param {Function} [el.highDownOnUpdate] See `graphic.setAsHighDownDispatcher`.
+ * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`.
+ */
+
+
+function setHoverStyle(el, hoverStyle) {
+  setAsHighDownDispatcher(el, true);
+  traverseUpdate(el, setElementHoverStyle, hoverStyle);
+}
+/**
+ * @param {module:zrender/Element} el
+ * @param {Function} [el.highDownOnUpdate] Called when state updated.
+ *        Since `setHoverStyle` has the constraint that it must be called after
+ *        all of the normal style updated, `highDownOnUpdate` is not needed to
+ *        trigger if both `fromState` and `toState` is 'normal', and needed to
+ *        trigger if both `fromState` and `toState` is 'emphasis', which enables
+ *        to sync outside style settings to "emphasis" state.
+ *        @this {string} This dispatcher `el`.
+ *        @param {string} fromState Can be "normal" or "emphasis".
+ *               `fromState` might equal to `toState`,
+ *               for example, when this method is called when `el` is
+ *               on "emphasis" state.
+ *        @param {string} toState Can be "normal" or "emphasis".
+ *
+ *        FIXME
+ *        CAUTION: Do not expose `highDownOnUpdate` outside echarts.
+ *        Because it is not a complete solution. The update
+ *        listener should not have been mount in element,
+ *        and the normal/emphasis state should not have
+ *        mantained on elements.
+ *
+ * @param {boolean} [el.highDownSilentOnTouch=false]
+ *        In touch device, mouseover event will be trigger on touchstart event
+ *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can
+ *        conveniently 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, `highDownSilentOnTouch` should be used to disable
+ *        hover-highlight on touch device.
+ * @param {boolean} [asDispatcher=true] If `false`, do not set as "highDownDispatcher".
+ */
+
+
+function setAsHighDownDispatcher(el, asDispatcher) {
+  var disable = asDispatcher === false; // Make `highDownSilentOnTouch` and `highDownOnUpdate` only work after
+  // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.
+
+  el.__highDownSilentOnTouch = el.highDownSilentOnTouch;
+  el.__highDownOnUpdate = el.highDownOnUpdate; // Simple optimize, since this method might be
+  // called for each elements of a group in some cases.
+
+  if (!disable || el.__highDownDispatcher) {
+    var method = disable ? 'off' : 'on'; // Duplicated function will be auto-ignored, see Eventful.js.
+
+    el[method]('mouseover', onElementMouseOver)[method]('mouseout', onElementMouseOut); // Emphasis, normal can be triggered manually by API or other components like hover link.
+
+    el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent); // Also keep previous record.
+
+    el.__highByOuter = el.__highByOuter || 0;
+    el.__highDownDispatcher = !disable;
+  }
+}
+/**
+ * @param {module:zrender/src/Element} el
+ * @return {boolean}
+ */
+
+
+function isHighDownDispatcher(el) {
+  return !!(el && el.__highDownDispatcher);
+}
+/**
+ * Support hightlight/downplay record on each elements.
+ * For the case: hover highlight/downplay (legend, visualMap, ...) and
+ * user triggerred hightlight/downplay should not conflict.
+ * Only all of the highlightDigit cleared, return to normal.
+ * @param {string} highlightKey
+ * @return {number} highlightDigit
+ */
+
+
+function getHighlightDigit(highlightKey) {
+  var highlightDigit = _highlightKeyMap[highlightKey];
+
+  if (highlightDigit == null && _highlightNextDigit <= 32) {
+    highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;
+  }
+
+  return highlightDigit;
+}
+/**
+ * See more info in `setTextStyleCommon`.
+ * @param {Object|module:zrender/graphic/Style} normalStyle
+ * @param {Object} emphasisStyle
+ * @param {module:echarts/model/Model} normalModel
+ * @param {module:echarts/model/Model} emphasisModel
+ * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.
+ * @param {string|Function} [opt.defaultText]
+ * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by
+ *      `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
+ * @param {number} [opt.labelDataIndex] Fetch text by
+ *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
+ * @param {number} [opt.labelDimIndex] Fetch text by
+ *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
+ * @param {string} [opt.labelProp] Fetch text by
+ *      `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`
+ * @param {Object} [normalSpecified]
+ * @param {Object} [emphasisSpecified]
+ */
+
+
+function setLabelStyle(normalStyle, emphasisStyle, normalModel, emphasisModel, opt, normalSpecified, emphasisSpecified) {
+  opt = opt || EMPTY_OBJ;
+  var labelFetcher = opt.labelFetcher;
+  var labelDataIndex = opt.labelDataIndex;
+  var labelDimIndex = opt.labelDimIndex;
+  var labelProp = opt.labelProp; // This scenario, `label.normal.show = true; label.emphasis.show = false`,
+  // is not supported util someone requests.
+
+  var showNormal = normalModel.getShallow('show');
+  var showEmphasis = emphasisModel.getShallow('show'); // Consider performance, only fetch label when necessary.
+  // If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,
+  // label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.
+
+  var baseText;
+
+  if (showNormal || showEmphasis) {
+    if (labelFetcher) {
+      baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, labelProp);
+    }
+
+    if (baseText == null) {
+      baseText = zrUtil.isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt) : opt.defaultText;
+    }
+  }
+
+  var normalStyleText = showNormal ? baseText : null;
+  var emphasisStyleText = showEmphasis ? zrUtil.retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, 'emphasis', null, labelDimIndex, labelProp) : null, baseText) : null; // Optimize: If style.text is null, text will not be drawn.
+
+  if (normalStyleText != null || emphasisStyleText != null) {
+    // Always set `textStyle` even if `normalStyle.text` is null, because default
+    // values have to be set on `normalStyle`.
+    // If we set default values on `emphasisStyle`, consider case:
+    // Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`
+    // Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`
+    // Then the 'red' will not work on emphasis.
+    setTextStyle(normalStyle, normalModel, normalSpecified, opt);
+    setTextStyle(emphasisStyle, emphasisModel, emphasisSpecified, opt, true);
+  }
+
+  normalStyle.text = normalStyleText;
+  emphasisStyle.text = emphasisStyleText;
+}
+/**
+ * Modify label style manually.
+ * Only works after `setLabelStyle` and `setElementHoverStyle` called.
+ *
+ * @param {module:zrender/src/Element} el
+ * @param {Object} [normalStyleProps] optional
+ * @param {Object} [emphasisStyleProps] optional
+ */
+
+
+function modifyLabelStyle(el, normalStyleProps, emphasisStyleProps) {
+  var elStyle = el.style;
+
+  if (normalStyleProps) {
+    rollbackDefaultTextStyle(elStyle);
+    el.setStyle(normalStyleProps);
+    applyDefaultTextStyle(elStyle);
+  }
+
+  elStyle = el.__hoverStl;
+
+  if (emphasisStyleProps && elStyle) {
+    rollbackDefaultTextStyle(elStyle);
+    zrUtil.extend(elStyle, emphasisStyleProps);
+    applyDefaultTextStyle(elStyle);
+  }
+}
+/**
+ * Set basic textStyle properties.
+ * See more info in `setTextStyleCommon`.
+ * @param {Object|module:zrender/graphic/Style} textStyle
+ * @param {module:echarts/model/Model} model
+ * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.
+ * @param {Object} [opt] See `opt` of `setTextStyleCommon`.
+ * @param {boolean} [isEmphasis]
+ */
+
+
+function setTextStyle(textStyle, textStyleModel, specifiedTextStyle, opt, isEmphasis) {
+  setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis);
+  specifiedTextStyle && zrUtil.extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
+
+  return textStyle;
+}
+/**
+ * Set text option in the style.
+ * See more info in `setTextStyleCommon`.
+ * @deprecated
+ * @param {Object} textStyle
+ * @param {module:echarts/model/Model} labelModel
+ * @param {string|boolean} defaultColor Default text color.
+ *        If set as false, it will be processed as a emphasis style.
+ */
+
+
+function setText(textStyle, labelModel, defaultColor) {
+  var opt = {
+    isRectText: true
+  };
+  var isEmphasis;
+
+  if (defaultColor === false) {
+    isEmphasis = true;
+  } else {
+    // Support setting color as 'auto' to get visual color.
+    opt.autoColor = defaultColor;
+  }
+
+  setTextStyleCommon(textStyle, labelModel, opt, isEmphasis); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);
+}
+/**
+ * The uniform entry of set text style, that is, retrieve style definitions
+ * from `model` and set to `textStyle` object.
+ *
+ * Never in merge mode, but in overwrite mode, that is, all of the text style
+ * properties will be set. (Consider the states of normal and emphasis and
+ * default value can be adopted, merge would make the logic too complicated
+ * to manage.)
+ *
+ * The `textStyle` object can either be a plain object or an instance of
+ * `zrender/src/graphic/Style`, and either be the style of normal or emphasis.
+ * After this mothod called, the `textStyle` object can then be used in
+ * `el.setStyle(textStyle)` or `el.hoverStyle = textStyle`.
+ *
+ * Default value will be adopted and `insideRollbackOpt` will be created.
+ * See `applyDefaultTextStyle` `rollbackDefaultTextStyle` for more details.
+ *
+ * opt: {
+ *      disableBox: boolean, Whether diable drawing box of block (outer most).
+ *      isRectText: boolean,
+ *      autoColor: string, specify a color when color is 'auto',
+ *              for textFill, textStroke, textBackgroundColor, and textBorderColor.
+ *              If autoColor specified, it is used as default textFill.
+ *      useInsideStyle:
+ *              `true`: Use inside style (textFill, textStroke, textStrokeWidth)
+ *                  if `textFill` is not specified.
+ *              `false`: Do not use inside style.
+ *              `null/undefined`: use inside style if `isRectText` is true and
+ *                  `textFill` is not specified and textPosition contains `'inside'`.
+ *      forceRich: boolean
+ * }
+ */
+
+
+function setTextStyleCommon(textStyle, textStyleModel, opt, isEmphasis) {
+  // Consider there will be abnormal when merge hover style to normal style if given default value.
+  opt = opt || EMPTY_OBJ;
+
+  if (opt.isRectText) {
+    var textPosition;
+
+    if (opt.getTextPosition) {
+      textPosition = opt.getTextPosition(textStyleModel, isEmphasis);
+    } else {
+      textPosition = textStyleModel.getShallow('position') || (isEmphasis ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used
+      // in bar series, and magric type should be considered.
+
+      textPosition === 'outside' && (textPosition = 'top');
+    }
+
+    textStyle.textPosition = textPosition;
+    textStyle.textOffset = textStyleModel.getShallow('offset');
+    var labelRotate = textStyleModel.getShallow('rotate');
+    labelRotate != null && (labelRotate *= Math.PI / 180);
+    textStyle.textRotation = labelRotate;
+    textStyle.textDistance = zrUtil.retrieve2(textStyleModel.getShallow('distance'), isEmphasis ? null : 5);
+  }
+
+  var ecModel = textStyleModel.ecModel;
+  var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case:
+  // {
+  //     data: [{
+  //         value: 12,
+  //         label: {
+  //             rich: {
+  //                 // no 'a' here but using parent 'a'.
+  //             }
+  //         }
+  //     }],
+  //     rich: {
+  //         a: { ... }
+  //     }
+  // }
+
+  var richItemNames = getRichItemNames(textStyleModel);
+  var richResult;
+
+  if (richItemNames) {
+    richResult = {};
+
+    for (var name in richItemNames) {
+      if (richItemNames.hasOwnProperty(name)) {
+        // Cascade is supported in rich.
+        var richTextStyle = textStyleModel.getModel(['rich', name]); // In rich, never `disableBox`.
+        // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,
+        // the default color `'blue'` will not be adopted if no color declared in `rich`.
+        // That might confuses users. So probably we should put `textStyleModel` as the
+        // root ancestor of the `richTextStyle`. But that would be a break change.
+
+        setTokenTextStyle(richResult[name] = {}, richTextStyle, globalTextStyle, opt, isEmphasis);
+      }
+    }
+  }
+
+  textStyle.rich = richResult;
+  setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, true);
+
+  if (opt.forceRich && !opt.textStyle) {
+    opt.textStyle = {};
+  }
+
+  return textStyle;
+} // Consider case:
+// {
+//     data: [{
+//         value: 12,
+//         label: {
+//             rich: {
+//                 // no 'a' here but using parent 'a'.
+//             }
+//         }
+//     }],
+//     rich: {
+//         a: { ... }
+//     }
+// }
+
+
+function getRichItemNames(textStyleModel) {
+  // Use object to remove duplicated names.
+  var richItemNameMap;
+
+  while (textStyleModel && textStyleModel !== textStyleModel.ecModel) {
+    var rich = (textStyleModel.option || EMPTY_OBJ).rich;
+
+    if (rich) {
+      richItemNameMap = richItemNameMap || {};
+
+      for (var name in rich) {
+        if (rich.hasOwnProperty(name)) {
+          richItemNameMap[name] = 1;
+        }
+      }
+    }
+
+    textStyleModel = textStyleModel.parentModel;
+  }
+
+  return richItemNameMap;
+}
+
+function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isEmphasis, isBlock) {
+  // In merge mode, default value should not be given.
+  globalTextStyle = !isEmphasis && globalTextStyle || EMPTY_OBJ;
+  textStyle.textFill = getAutoColor(textStyleModel.getShallow('color'), opt) || globalTextStyle.color;
+  textStyle.textStroke = getAutoColor(textStyleModel.getShallow('textBorderColor'), opt) || globalTextStyle.textBorderColor;
+  textStyle.textStrokeWidth = zrUtil.retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth);
+
+  if (!isEmphasis) {
+    if (isBlock) {
+      textStyle.insideRollbackOpt = opt;
+      applyDefaultTextStyle(textStyle);
+    } // Set default finally.
+
+
+    if (textStyle.textFill == null) {
+      textStyle.textFill = opt.autoColor;
+    }
+  } // Do not use `getFont` here, because merge should be supported, where
+  // part of these properties may be changed in emphasis style, and the
+  // others should remain their original value got from normal style.
+
+
+  textStyle.fontStyle = textStyleModel.getShallow('fontStyle') || globalTextStyle.fontStyle;
+  textStyle.fontWeight = textStyleModel.getShallow('fontWeight') || globalTextStyle.fontWeight;
+  textStyle.fontSize = textStyleModel.getShallow('fontSize') || globalTextStyle.fontSize;
+  textStyle.fontFamily = textStyleModel.getShallow('fontFamily') || globalTextStyle.fontFamily;
+  textStyle.textAlign = textStyleModel.getShallow('align');
+  textStyle.textVerticalAlign = textStyleModel.getShallow('verticalAlign') || textStyleModel.getShallow('baseline');
+  textStyle.textLineHeight = textStyleModel.getShallow('lineHeight');
+  textStyle.textWidth = textStyleModel.getShallow('width');
+  textStyle.textHeight = textStyleModel.getShallow('height');
+  textStyle.textTag = textStyleModel.getShallow('tag');
+
+  if (!isBlock || !opt.disableBox) {
+    textStyle.textBackgroundColor = getAutoColor(textStyleModel.getShallow('backgroundColor'), opt);
+    textStyle.textPadding = textStyleModel.getShallow('padding');
+    textStyle.textBorderColor = getAutoColor(textStyleModel.getShallow('borderColor'), opt);
+    textStyle.textBorderWidth = textStyleModel.getShallow('borderWidth');
+    textStyle.textBorderRadius = textStyleModel.getShallow('borderRadius');
+    textStyle.textBoxShadowColor = textStyleModel.getShallow('shadowColor');
+    textStyle.textBoxShadowBlur = textStyleModel.getShallow('shadowBlur');
+    textStyle.textBoxShadowOffsetX = textStyleModel.getShallow('shadowOffsetX');
+    textStyle.textBoxShadowOffsetY = textStyleModel.getShallow('shadowOffsetY');
+  }
+
+  textStyle.textShadowColor = textStyleModel.getShallow('textShadowColor') || globalTextStyle.textShadowColor;
+  textStyle.textShadowBlur = textStyleModel.getShallow('textShadowBlur') || globalTextStyle.textShadowBlur;
+  textStyle.textShadowOffsetX = textStyleModel.getShallow('textShadowOffsetX') || globalTextStyle.textShadowOffsetX;
+  textStyle.textShadowOffsetY = textStyleModel.getShallow('textShadowOffsetY') || globalTextStyle.textShadowOffsetY;
+}
+
+function getAutoColor(color, opt) {
+  return color !== 'auto' ? color : opt && opt.autoColor ? opt.autoColor : null;
+}
+/**
+ * Give some default value to the input `textStyle` object, based on the current settings
+ * in this `textStyle` object.
+ *
+ * The Scenario:
+ * when text position is `inside` and `textFill` is not specified, we show
+ * text border by default for better view. But it should be considered that text position
+ * might be changed when hovering or being emphasis, where the `insideRollback` is used to
+ * restore the style.
+ *
+ * Usage (& NOTICE):
+ * When a style object (eithor plain object or instance of `zrender/src/graphic/Style`) is
+ * about to be modified on its text related properties, `rollbackDefaultTextStyle` should
+ * be called before the modification and `applyDefaultTextStyle` should be called after that.
+ * (For the case that all of the text related properties is reset, like `setTextStyleCommon`
+ * does, `rollbackDefaultTextStyle` is not needed to be called).
+ */
+
+
+function applyDefaultTextStyle(textStyle) {
+  var textPosition = textStyle.textPosition;
+  var opt = textStyle.insideRollbackOpt;
+  var insideRollback;
+
+  if (opt && textStyle.textFill == null) {
+    var autoColor = opt.autoColor;
+    var isRectText = opt.isRectText;
+    var useInsideStyle = opt.useInsideStyle;
+    var useInsideStyleCache = useInsideStyle !== false && (useInsideStyle === true || isRectText && textPosition // textPosition can be [10, 30]
+    && typeof textPosition === 'string' && textPosition.indexOf('inside') >= 0);
+    var useAutoColorCache = !useInsideStyleCache && autoColor != null; // All of the props declared in `CACHED_LABEL_STYLE_PROPERTIES` are to be cached.
+
+    if (useInsideStyleCache || useAutoColorCache) {
+      insideRollback = {
+        textFill: textStyle.textFill,
+        textStroke: textStyle.textStroke,
+        textStrokeWidth: textStyle.textStrokeWidth
+      };
+    }
+
+    if (useInsideStyleCache) {
+      textStyle.textFill = '#fff'; // Consider text with #fff overflow its container.
+
+      if (textStyle.textStroke == null) {
+        textStyle.textStroke = autoColor;
+        textStyle.textStrokeWidth == null && (textStyle.textStrokeWidth = 2);
+      }
+    }
+
+    if (useAutoColorCache) {
+      textStyle.textFill = autoColor;
+    }
+  } // Always set `insideRollback`, so that the previous one can be cleared.
+
+
+  textStyle.insideRollback = insideRollback;
+}
+/**
+ * Consider the case: in a scatter,
+ * label: {
+ *     normal: {position: 'inside'},
+ *     emphasis: {position: 'top'}
+ * }
+ * In the normal state, the `textFill` will be set as '#fff' for pretty view (see
+ * `applyDefaultTextStyle`), but when switching to emphasis state, the `textFill`
+ * should be retured to 'autoColor', but not keep '#fff'.
+ */
+
+
+function rollbackDefaultTextStyle(style) {
+  var insideRollback = style.insideRollback;
+
+  if (insideRollback) {
+    // Reset all of the props in `CACHED_LABEL_STYLE_PROPERTIES`.
+    style.textFill = insideRollback.textFill;
+    style.textStroke = insideRollback.textStroke;
+    style.textStrokeWidth = insideRollback.textStrokeWidth;
+    style.insideRollback = null;
+  }
+}
+
+function getFont(opt, ecModel) {
+  var gTextStyleModel = ecModel && ecModel.getModel('textStyle');
+  return zrUtil.trim([// FIXME in node-canvas fontWeight is before fontStyle
+  opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' '));
+}
+
+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, !!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.
+ *
+ * Caution: this method will stop previous animation.
+ * So do not use this method to one element twice before
+ * animation starts, unless you know what you are doing.
+ *
+ * @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!'); });
+ */
+
+
+function updateProps(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.
+ *
+ * Caution: this method will stop previous animation.
+ * So do not use this method to one element twice before
+ * animation starts, unless you know what you are doing.
+ *
+ * @param {module:zrender/Element} el
+ * @param {Object} props
+ * @param {module:echarts/model/Model} [animatableModel]
+ * @param {number} [dataIndex]
+ * @param {Function} cb
+ */
+
+
+function initProps(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]
+ */
+
+
+function getTransform(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]
+ */
+
+
+function applyTransform(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'
+ */
+
+
+function transformDirection(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 = 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.
+ */
+
+
+function groupTransition(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));
+        updateProps(el, newProp, animatableModel, el.dataIndex);
+      } // else {
+      //     if (el.previousProps) {
+      //         graphic.updateProps
+      //     }
+      // }
+
+    }
+  });
+}
+/**
+ * @param {Array.<Array.<number>>} points Like: [[23, 44], [53, 66], ...]
+ * @param {Object} rect {x, y, width, height}
+ * @return {Array.<Array.<number>>} A new clipped points.
+ */
+
+
+function clipPointsByRect(points, rect) {
+  // FIXME: this way migth be incorrect when grpahic clipped by a corner.
+  // and when element have border.
+  return zrUtil.map(points, function (point) {
+    var x = point[0];
+    x = mathMax(x, rect.x);
+    x = mathMin(x, rect.x + rect.width);
+    var y = point[1];
+    y = mathMax(y, rect.y);
+    y = mathMin(y, rect.y + rect.height);
+    return [x, y];
+  });
+}
+/**
+ * @param {Object} targetRect {x, y, width, height}
+ * @param {Object} rect {x, y, width, height}
+ * @return {Object} A new clipped rect. If rect size are negative, return undefined.
+ */
+
+
+function clipRectByRect(targetRect, rect) {
+  var x = mathMax(targetRect.x, rect.x);
+  var x2 = mathMin(targetRect.x + targetRect.width, rect.x + rect.width);
+  var y = mathMax(targetRect.y, rect.y);
+  var y2 = mathMin(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border,
+  // should be painted. So return undefined.
+
+  if (x2 >= x && y2 >= y) {
+    return {
+      x: x,
+      y: y,
+      width: x2 - x,
+      height: y2 - y
+    };
+  }
+}
+/**
+ * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.
+ * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.
+ * @param {Object} [rect] {x, y, width, height}
+ * @return {module:zrender/Element} Icon path or image element.
+ */
+
+
+function createIcon(iconStr, opt, rect) {
+  opt = zrUtil.extend({
+    rectHover: true
+  }, opt);
+  var style = opt.style = {
+    strokeNoScale: true
+  };
+  rect = rect || {
+    x: -1,
+    y: -1,
+    width: 2,
+    height: 2
+  };
+
+  if (iconStr) {
+    return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), zrUtil.defaults(style, rect), new ZImage(opt)) : makePath(iconStr.replace('path://', ''), opt, rect, 'center');
+  }
+}
+/**
+ * Return `true` if the given line (line `a`) and the given polygon
+ * are intersect.
+ * Note that we do not count colinear as intersect here because no
+ * requirement for that. We could do that if required in future.
+ *
+ * @param {number} a1x
+ * @param {number} a1y
+ * @param {number} a2x
+ * @param {number} a2y
+ * @param {Array.<Array.<number>>} points Points of the polygon.
+ * @return {boolean}
+ */
+
+
+function linePolygonIntersect(a1x, a1y, a2x, a2y, points) {
+  for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
+    var p = points[i];
+
+    if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) {
+      return true;
+    }
+
+    p2 = p;
+  }
+}
+/**
+ * Return `true` if the given two lines (line `a` and line `b`)
+ * are intersect.
+ * Note that we do not count colinear as intersect here because no
+ * requirement for that. We could do that if required in future.
+ *
+ * @param {number} a1x
+ * @param {number} a1y
+ * @param {number} a2x
+ * @param {number} a2y
+ * @param {number} b1x
+ * @param {number} b1y
+ * @param {number} b2x
+ * @param {number} b2y
+ * @return {boolean}
+ */
+
+
+function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) {
+  // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`.
+  var mx = a2x - a1x;
+  var my = a2y - a1y;
+  var nx = b2x - b1x;
+  var ny = b2y - b1y; // `vec_m` and `vec_n` are parallel iff
+  //     exising `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`.
+
+  var nmCrossProduct = crossProduct2d(nx, ny, mx, my);
+
+  if (nearZero(nmCrossProduct)) {
+    return false;
+  } // `vec_m` and `vec_n` are intersect iff
+  //     existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`,
+  //     such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)`
+  //           and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`.
+
+
+  var b1a1x = a1x - b1x;
+  var b1a1y = a1y - b1y;
+  var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct;
+
+  if (q < 0 || q > 1) {
+    return false;
+  }
+
+  var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct;
+
+  if (p < 0 || p > 1) {
+    return false;
+  }
+
+  return true;
+}
+/**
+ * Cross product of 2-dimension vector.
+ */
+
+
+function crossProduct2d(x1, y1, x2, y2) {
+  return x1 * y2 - x2 * y1;
+}
+
+function nearZero(val) {
+  return val <= 1e-6 && val >= -1e-6;
+} // Register built-in shapes. These shapes might be overwirtten
+// by users, although we do not recommend that.
+
+
+registerShape('circle', Circle);
+registerShape('sector', Sector);
+registerShape('ring', Ring);
+registerShape('polygon', Polygon);
+registerShape('polyline', Polyline);
+registerShape('rect', Rect);
+registerShape('line', Line);
+registerShape('bezierCurve', BezierCurve);
+registerShape('arc', Arc);
+exports.Z2_EMPHASIS_LIFT = Z2_EMPHASIS_LIFT;
+exports.CACHED_LABEL_STYLE_PROPERTIES = CACHED_LABEL_STYLE_PROPERTIES;
+exports.extendShape = extendShape;
+exports.extendPath = extendPath;
+exports.registerShape = registerShape;
+exports.getShapeClass = getShapeClass;
+exports.makePath = makePath;
+exports.makeImage = makeImage;
+exports.mergePath = mergePath;
+exports.resizePath = resizePath;
+exports.subPixelOptimizeLine = subPixelOptimizeLine;
+exports.subPixelOptimizeRect = subPixelOptimizeRect;
+exports.subPixelOptimize = subPixelOptimize;
+exports.setElementHoverStyle = setElementHoverStyle;
+exports.setHoverStyle = setHoverStyle;
+exports.setAsHighDownDispatcher = setAsHighDownDispatcher;
+exports.isHighDownDispatcher = isHighDownDispatcher;
+exports.getHighlightDigit = getHighlightDigit;
+exports.setLabelStyle = setLabelStyle;
+exports.modifyLabelStyle = modifyLabelStyle;
+exports.setTextStyle = setTextStyle;
+exports.setText = setText;
+exports.getFont = getFont;
+exports.updateProps = updateProps;
+exports.initProps = initProps;
+exports.getTransform = getTransform;
+exports.applyTransform = applyTransform;
+exports.transformDirection = transformDirection;
+exports.groupTransition = groupTransition;
+exports.clipPointsByRect = clipPointsByRect;
+exports.clipRectByRect = clipRectByRect;
+exports.createIcon = createIcon;
+exports.linePolygonIntersect = linePolygonIntersect;
+exports.lineLineIntersect = lineLineIntersect;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/util/model.js":
+/*!************************************************!*\
+  !*** ./node_modules/echarts/lib/util/model.js ***!
+  \************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var zrUtil = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var env = __webpack_require__(/*! zrender/lib/core/env */ "./node_modules/zrender/lib/core/env.js");
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+var each = zrUtil.each;
+var isObject = zrUtil.isObject;
+var isArray = zrUtil.isArray;
+/**
+ * Make the name displayable. But we should
+ * make sure it is not duplicated with user
+ * specified name, so use '\0';
+ */
+
+var DUMMY_COMPONENT_NAME_PREFIX = 'series\0';
+/**
+ * If value is not array, then translate it to array.
+ * @param  {*} value
+ * @return {Array} [value] or value
+ */
+
+function normalizeToArray(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: {
+ *          show: false,
+ *          position: 'outside',
+ *          fontSize: 18
+ *     },
+ *     emphasis: {
+ *          label: { show: true }
+ *     }
+ * @param {Object} opt
+ * @param {string} key
+ * @param {Array.<string>} subOpts
+ */
+
+
+function defaultEmphasis(opt, key, subOpts) {
+  // Caution: performance sensitive.
+  if (opt) {
+    opt[key] = opt[key] || {};
+    opt.emphasis = opt.emphasis || {};
+    opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal
+
+    for (var i = 0, len = subOpts.length; i < len; i++) {
+      var subOptName = subOpts[i];
+
+      if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) {
+        opt.emphasis[key][subOptName] = opt[key][subOptName];
+      }
+    }
+  }
+}
+
+var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([
+//     'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter',
+//     'fontStyle', 'fontWeight', 'fontSize', 'fontFamily',
+//     // FIXME: deprecated, check and remove it.
+//     'textStyle'
+// ]);
+
+/**
+ * The method do not ensure performance.
+ * 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>}
+ */
+
+function getDataItemValue(dataItem) {
+  return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem;
+}
+/**
+ * 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
+ */
+
+
+function isDataItemOption(dataItem) {
+  return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array
+  // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));
+}
+/**
+ * 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.
+ */
+
+
+function mappingToExists(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 && !isIdInner(cptOption) && !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.
+      && !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.
+ */
+
+
+function makeIdAndName(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 = zrUtil.createHashMap();
+  each(mapResult, function (item, index) {
+    var existCpt = item.exist;
+    existCpt && idMap.set(existCpt.id, item);
+  });
+  each(mapResult, function (item, index) {
+    var opt = item.option;
+    zrUtil.assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id));
+    opt && opt.id != null && idMap.set(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 // Avoid diffferent series has the same name,
+    // because name may be used like in color pallet.
+    : DUMMY_COMPONENT_NAME_PREFIX + index;
+
+    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.get(keyInfo.id));
+    }
+
+    idMap.set(keyInfo.id, item);
+  });
+}
+
+function isNameSpecified(componentModel) {
+  var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0.
+
+  return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));
+}
+/**
+ * @public
+ * @param {Object} cptOption
+ * @return {boolean}
+ */
+
+
+function isIdInner(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]
+ */
+
+
+function compressBatches(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 = 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.
+ */
+
+
+function queryDataIndex(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 inner = zrUitl.makeInner();
+ *
+ * function some1(hostObj) {
+ *      inner(hostObj).someProperty = 1212;
+ *      ...
+ * }
+ * function some2() {
+ *      var fields = inner(this);
+ *      fields.someProperty1 = 1212;
+ *      fields.someProperty2 = 'xx';
+ *      ...
+ * }
+ *
+ * @return {Function}
+ */
+
+
+function makeInner() {
+  // Consider different scope by es module import.
+  var key = '__\0ec_inner_' + innerUniqueIndex++ + '_' + Math.random().toFixed(5);
+  return function (hostObj) {
+    return hostObj[key] || (hostObj[key] = {});
+  };
+}
+
+var innerUniqueIndex = 0;
+/**
+ * @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
+ *            ...
+ *        }
+ */
+
+function parseFinder(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);
+}
+
+function setAttribute(dom, key, value) {
+  dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value;
+}
+
+function getAttribute(dom, key) {
+  return dom.getAttribute ? dom.getAttribute(key) : dom[key];
+}
+
+function getTooltipRenderMode(renderModeOption) {
+  if (renderModeOption === 'auto') {
+    // Using html when `document` exists, use richText otherwise
+    return env.domSupported ? 'html' : 'richText';
+  } else {
+    return renderModeOption || 'html';
+  }
+}
+/**
+ * Group a list by key.
+ *
+ * @param {Array} array
+ * @param {Function} getKey
+ *        param {*} Array item
+ *        return {string} key
+ * @return {Object} Result
+ *        {Array}: keys,
+ *        {module:zrender/core/util/HashMap} buckets: {key -> Array}
+ */
+
+
+function groupData(array, getKey) {
+  var buckets = zrUtil.createHashMap();
+  var keys = [];
+  zrUtil.each(array, function (item) {
+    var key = getKey(item);
+    (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item);
+  });
+  return {
+    keys: keys,
+    buckets: buckets
+  };
+}
+
+exports.normalizeToArray = normalizeToArray;
+exports.defaultEmphasis = defaultEmphasis;
+exports.TEXT_STYLE_OPTIONS = TEXT_STYLE_OPTIONS;
+exports.getDataItemValue = getDataItemValue;
+exports.isDataItemOption = isDataItemOption;
+exports.mappingToExists = mappingToExists;
+exports.makeIdAndName = makeIdAndName;
+exports.isNameSpecified = isNameSpecified;
+exports.isIdInner = isIdInner;
+exports.compressBatches = compressBatches;
+exports.queryDataIndex = queryDataIndex;
+exports.makeInner = makeInner;
+exports.parseFinder = parseFinder;
+exports.setAttribute = setAttribute;
+exports.getAttribute = getAttribute;
+exports.getTooltipRenderMode = getTooltipRenderMode;
+exports.groupData = groupData;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/util/symbol.js":
+/*!*************************************************!*\
+  !*** ./node_modules/echarts/lib/util/symbol.js ***!
+  \*************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var zrUtil = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var graphic = __webpack_require__(/*! ./graphic */ "./node_modules/echarts/lib/util/graphic.js");
+
+var BoundingRect = __webpack_require__(/*! zrender/lib/core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+var _text = __webpack_require__(/*! zrender/lib/contain/text */ "./node_modules/zrender/lib/contain/text.js");
+
+var calculateTextPosition = _text.calculateTextPosition;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+// Symbol factory
+
+/**
+ * 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);
+    var cpLen = r * 0.6;
+    var cpLen2 = r * 0.7;
+    path.moveTo(x - dx, cy + dy);
+    path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle);
+    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 = {};
+zrUtil.each(symbolCtors, function (Ctor, name) {
+  symbolBuildProxies[name] = new Ctor();
+});
+var SymbolClz = graphic.extendShape({
+  type: 'symbol',
+  shape: {
+    symbolType: '',
+    x: 0,
+    y: 0,
+    width: 0,
+    height: 0
+  },
+  calculateTextPosition: function (out, style, rect) {
+    var res = calculateTextPosition(out, style, rect);
+    var shape = this.shape;
+
+    if (shape && shape.symbolType === 'pin' && style.textPosition === 'inside') {
+      res.y = rect.y + rect.height * 0.4;
+    }
+
+    return res;
+  },
+  buildPath: function (ctx, shape, inBundle) {
+    var symbolType = shape.symbolType;
+
+    if (symbolType !== 'none') {
+      var proxySymbol = symbolBuildProxies[symbolType];
+
+      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
+
+function symbolPathSetColor(color, innerColor) {
+  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 = innerColor || '#fff';
+    } else {
+      // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ?
+      symbolStyle.fill && (symbolStyle.fill = color);
+      symbolStyle.stroke && (symbolStyle.stroke = color);
+    }
+
+    this.dirty(false);
+  }
+}
+/**
+ * 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
+ * @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
+ *                            for path and image only.
+ */
+
+
+function createSymbol(symbolType, x, y, w, h, color, keepAspect) {
+  // 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 = graphic.makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
+  } else if (symbolType.indexOf('path://') === 0) {
+    symbolPath = graphic.makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover');
+  } else {
+    symbolPath = new SymbolClz({
+      shape: {
+        symbolType: symbolType,
+        x: x,
+        y: y,
+        width: w,
+        height: h
+      }
+    });
+  }
+
+  symbolPath.__isEmptyBrush = isEmpty;
+  symbolPath.setColor = symbolPathSetColor;
+  symbolPath.setColor(color);
+  return symbolPath;
+}
+
+exports.createSymbol = createSymbol;
+
+/***/ }),
+
+/***/ "./node_modules/echarts/lib/visual/dataColor.js":
+/*!******************************************************!*\
+  !*** ./node_modules/echarts/lib/visual/dataColor.js ***!
+  \******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+var _util = __webpack_require__(/*! zrender/lib/core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var createHashMap = _util.createHashMap;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+// Pick color from palette for each data item.
+// Applicable for charts that require applying color palette
+// in data level (like pie, funnel, chord).
+function _default(seriesType) {
+  return {
+    getTargetSeries: function (ecModel) {
+      // Pie and funnel may use diferrent scope
+      var paletteScope = {};
+      var seiresModelMap = createHashMap();
+      ecModel.eachSeriesByType(seriesType, function (seriesModel) {
+        seriesModel.__paletteScope = paletteScope;
+        seiresModelMap.set(seriesModel.uid, seriesModel);
+      });
+      return seiresModelMap;
+    },
+    reset: function (seriesModel, ecModel) {
+      var dataAll = seriesModel.getRawData();
+      var idxMap = {};
+      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);
+        var singleDataBorderColor = filteredIdx != null && data.getItemVisual(filteredIdx, 'borderColor', true);
+        var itemModel;
+
+        if (!singleDataColor || !singleDataBorderColor) {
+          // FIXME Performance
+          itemModel = dataAll.getItemModel(rawIdx);
+        }
+
+        if (!singleDataColor) {
+          var color = itemModel.get('itemStyle.color') || seriesModel.getColorFromPalette(dataAll.getName(rawIdx) || rawIdx + '', seriesModel.__paletteScope, dataAll.count()); // Data is not filtered
+
+          if (filteredIdx != null) {
+            data.setItemVisual(filteredIdx, 'color', color);
+          }
+        }
+
+        if (!singleDataBorderColor) {
+          var borderColor = itemModel.get('itemStyle.borderColor'); // Data is not filtered
+
+          if (filteredIdx != null) {
+            data.setItemVisual(filteredIdx, 'borderColor', borderColor);
+          }
+        }
+      });
+    }
+  };
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/webpack/buildin/global.js":
+/*!***********************************!*\
+  !*** (webpack)/buildin/global.js ***!
+  \***********************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+var g;
+
+// This works in non-strict mode
+g = (function() {
+	return this;
+})();
+
+try {
+	// This works if eval is allowed (see CSP)
+	g = g || new Function("return this")();
+} catch (e) {
+	// This works if the window reference is available
+	if (typeof window === "object") g = window;
+}
+
+// g can still be undefined, but nothing to do about it...
+// We return undefined, instead of nothing here, so it's
+// easier to handle this case. if(!global) { ...}
+
+module.exports = g;
+
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/Element.js":
+/*!*********************************************!*\
+  !*** ./node_modules/zrender/lib/Element.js ***!
+  \*********************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var guid = __webpack_require__(/*! ./core/guid */ "./node_modules/zrender/lib/core/guid.js");
+
+var Eventful = __webpack_require__(/*! ./mixin/Eventful */ "./node_modules/zrender/lib/mixin/Eventful.js");
+
+var Transformable = __webpack_require__(/*! ./mixin/Transformable */ "./node_modules/zrender/lib/mixin/Transformable.js");
+
+var Animatable = __webpack_require__(/*! ./mixin/Animatable */ "./node_modules/zrender/lib/mixin/Animatable.js");
+
+var zrUtil = __webpack_require__(/*! ./core/util */ "./node_modules/zrender/lib/core/util.js");
+
+/**
+ * @alias module:zrender/Element
+ * @constructor
+ * @extends {module:zrender/mixin/Animatable}
+ * @extends {module:zrender/mixin/Transformable}
+ * @extends {module:zrender/mixin/Eventful}
+ */
+var Element = function (opts) {
+  // jshint ignore:line
+  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,
+
+  /**
+   * 是否是 Group
+   * @type {boolean}
+   */
+  isGroup: false,
+
+  /**
+   * 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);
+var _default = Element;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/animation/Animator.js":
+/*!********************************************************!*\
+  !*** ./node_modules/zrender/lib/animation/Animator.js ***!
+  \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Clip = __webpack_require__(/*! ./Clip */ "./node_modules/zrender/lib/animation/Clip.js");
+
+var color = __webpack_require__(/*! ../tool/color */ "./node_modules/zrender/lib/tool/color.js");
+
+var _util = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var isArrayLike = _util.isArrayLike;
+
+/**
+ * @module echarts/animation/Animator
+ */
+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 = len && 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 getArrayDim(keyframes) {
+  var lastValue = keyframes[keyframes.length - 1].value;
+  return isArrayLike(lastValue && lastValue[0]) ? 2 : 1;
+}
+
+function createTrackClip(animator, easing, oneTrackDone, keyframes, propName, forceAnimate) {
+  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 ? getArrayDim(keyframes) : 0;
+  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 (!forceAnimate && 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 = {
+  /**
+   * Set Animation keyframe
+   * @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);
+    }
+  },
+
+  /**
+   * Start the animation
+   * @param  {string|Function} [easing]
+   *         动画缓动函数,详见{@link module:zrender/animation/easing}
+   * @param  {boolean} forceAnimate
+   * @return {module:zrender/animation/Animator}
+   */
+  start: function (easing, forceAnimate) {
+    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, forceAnimate);
+
+      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);
+        }
+      };
+    } // This optimization will help the case that in the upper application
+    // the view may be refreshed frequently, where animation will be
+    // called repeatly but nothing changed.
+
+
+    if (!clipCount) {
+      this._doneCallback();
+    }
+
+    return this;
+  },
+
+  /**
+   * Stop animation
+   * @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;
+  },
+
+  /**
+   * Set when animation delay starts
+   * @param  {number} time 单位ms
+   * @return {module:zrender/animation/Animator}
+   */
+  delay: function (time) {
+    this._delay = time;
+    return this;
+  },
+
+  /**
+   * Add callback for animation end
+   * @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;
+  }
+};
+var _default = Animator;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/animation/Clip.js":
+/*!****************************************************!*\
+  !*** ./node_modules/zrender/lib/animation/Clip.js ***!
+  \****************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var easingFuncs = __webpack_require__(/*! ./easing */ "./node_modules/zrender/lib/animation/easing.js");
+
+/**
+ * 动画主控制器
+ * @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
+ */
+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;
+  }
+};
+var _default = Clip;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/animation/easing.js":
+/*!******************************************************!*\
+  !*** ./node_modules/zrender/lib/animation/easing.js ***!
+  \******************************************************/
+/*! no static exports found */
+/***/ (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;
+  }
+};
+var _default = easing;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/config.js":
+/*!********************************************!*\
+  !*** ./node_modules/zrender/lib/config.js ***!
+  \********************************************/
+/*! no static exports found */
+/***/ (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)
+ */
+
+/**
+ * Debug log mode:
+ * 0: Do nothing, for release.
+ * 1: console.error, for debug.
+ */
+
+
+var debugMode = 0; // retina 屏幕优化
+
+var devicePixelRatio = dpr;
+exports.debugMode = debugMode;
+exports.devicePixelRatio = devicePixelRatio;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/arc.js":
+/*!*************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/arc.js ***!
+  \*************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _util = __webpack_require__(/*! ./util */ "./node_modules/zrender/lib/contain/util.js");
+
+var normalizeRadian = _util.normalizeRadian;
+var PI2 = Math.PI * 2;
+/**
+ * 圆弧描边包含判断
+ * @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}
+ */
+
+function containStroke(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;
+}
+
+exports.containStroke = containStroke;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/cubic.js":
+/*!***************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/cubic.js ***!
+  \***************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var curve = __webpack_require__(/*! ../core/curve */ "./node_modules/zrender/lib/core/curve.js");
+
+/**
+ * 三次贝塞尔曲线描边包含判断
+ * @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}
+ */
+function containStroke(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;
+}
+
+exports.containStroke = containStroke;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/line.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/line.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(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}
+ */
+function containStroke(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;
+}
+
+exports.containStroke = containStroke;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/path.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/path.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var PathProxy = __webpack_require__(/*! ../core/PathProxy */ "./node_modules/zrender/lib/core/PathProxy.js");
+
+var line = __webpack_require__(/*! ./line */ "./node_modules/zrender/lib/contain/line.js");
+
+var cubic = __webpack_require__(/*! ./cubic */ "./node_modules/zrender/lib/contain/cubic.js");
+
+var quadratic = __webpack_require__(/*! ./quadratic */ "./node_modules/zrender/lib/contain/quadratic.js");
+
+var arc = __webpack_require__(/*! ./arc */ "./node_modules/zrender/lib/contain/arc.js");
+
+var _util = __webpack_require__(/*! ./util */ "./node_modules/zrender/lib/contain/util.js");
+
+var normalizeRadian = _util.normalizeRadian;
+
+var curve = __webpack_require__(/*! ../core/curve */ "./node_modules/zrender/lib/core/curve.js");
+
+var windingLine = __webpack_require__(/*! ./windingLine */ "./node_modules/zrender/lib/contain/windingLine.js");
+
+var CMD = PathProxy.CMD;
+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_;
+    var 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 (line.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 旋转
+
+        i += 1;
+        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 (line.containStroke(x0, y0, x1, y0, lineWidth, x, y) || line.containStroke(x1, y0, x1, y1, lineWidth, x, y) || line.containStroke(x1, y1, x0, y1, lineWidth, x, y) || line.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 (line.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;
+}
+
+function contain(pathData, x, y) {
+  return containPath(pathData, 0, false, x, y);
+}
+
+function containStroke(pathData, lineWidth, x, y) {
+  return containPath(pathData, lineWidth, true, x, y);
+}
+
+exports.contain = contain;
+exports.containStroke = containStroke;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/quadratic.js":
+/*!*******************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/quadratic.js ***!
+  \*******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _curve = __webpack_require__(/*! ../core/curve */ "./node_modules/zrender/lib/core/curve.js");
+
+var quadraticProjectPoint = _curve.quadraticProjectPoint;
+
+/**
+ * 二次贝塞尔曲线描边包含判断
+ * @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}
+ */
+function containStroke(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 = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null);
+  return d <= _l / 2;
+}
+
+exports.containStroke = containStroke;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/text.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/text.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var BoundingRect = __webpack_require__(/*! ../core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+var imageHelper = __webpack_require__(/*! ../graphic/helper/image */ "./node_modules/zrender/lib/graphic/helper/image.js");
+
+var _util = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var getContext = _util.getContext;
+var extend = _util.extend;
+var retrieve2 = _util.retrieve2;
+var retrieve3 = _util.retrieve3;
+var trim = _util.trim;
+var textWidthCache = {};
+var textWidthCacheCounter = 0;
+var TEXT_CACHE_MAX = 5000;
+var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g;
+var DEFAULT_FONT = '12px sans-serif'; // Avoid assign to an exported variable, for transforming to cjs.
+
+var methods = {};
+
+function $override(name, fn) {
+  methods[name] = fn;
+}
+/**
+ * @public
+ * @param {string} text
+ * @param {string} font
+ * @return {number} width
+ */
+
+
+function getWidth(text, font) {
+  font = font || DEFAULT_FONT;
+  var key = text + ':' + font;
+
+  if (textWidthCache[key]) {
+    return textWidthCache[key];
+  }
+
+  var textLines = (text + '').split('\n');
+  var width = 0;
+
+  for (var i = 0, l = textLines.length; i < l; i++) {
+    // textContain.measureText may be overrided in SVG or VML
+    width = Math.max(measureText(textLines[i], font).width, width);
+  }
+
+  if (textWidthCacheCounter > TEXT_CACHE_MAX) {
+    textWidthCacheCounter = 0;
+    textWidthCache = {};
+  }
+
+  textWidthCacheCounter++;
+  textWidthCache[key] = width;
+  return width;
+}
+/**
+ * @public
+ * @param {string} text
+ * @param {string} font
+ * @param {string} [textAlign='left']
+ * @param {string} [textVerticalAlign='top']
+ * @param {Array.<number>} [textPadding]
+ * @param {Object} [rich]
+ * @param {Object} [truncate]
+ * @return {Object} {x, y, width, height, lineHeight}
+ */
+
+
+function getBoundingRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate) {
+  return rich ? getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate) : getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, truncate);
+}
+
+function getPlainTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, truncate) {
+  var contentBlock = parsePlainText(text, font, textPadding, textLineHeight, truncate);
+  var outerWidth = getWidth(text, font);
+
+  if (textPadding) {
+    outerWidth += textPadding[1] + textPadding[3];
+  }
+
+  var outerHeight = contentBlock.outerHeight;
+  var x = adjustTextX(0, outerWidth, textAlign);
+  var y = adjustTextY(0, outerHeight, textVerticalAlign);
+  var rect = new BoundingRect(x, y, outerWidth, outerHeight);
+  rect.lineHeight = contentBlock.lineHeight;
+  return rect;
+}
+
+function getRichTextRect(text, font, textAlign, textVerticalAlign, textPadding, textLineHeight, rich, truncate) {
+  var contentBlock = parseRichText(text, {
+    rich: rich,
+    truncate: truncate,
+    font: font,
+    textAlign: textAlign,
+    textPadding: textPadding,
+    textLineHeight: textLineHeight
+  });
+  var outerWidth = contentBlock.outerWidth;
+  var outerHeight = contentBlock.outerHeight;
+  var x = adjustTextX(0, outerWidth, textAlign);
+  var y = adjustTextY(0, outerHeight, textVerticalAlign);
+  return new BoundingRect(x, y, outerWidth, outerHeight);
+}
+/**
+ * @public
+ * @param {number} x
+ * @param {number} width
+ * @param {string} [textAlign='left']
+ * @return {number} Adjusted x.
+ */
+
+
+function adjustTextX(x, width, textAlign) {
+  // FIXME Right to left language
+  if (textAlign === 'right') {
+    x -= width;
+  } else if (textAlign === 'center') {
+    x -= width / 2;
+  }
+
+  return x;
+}
+/**
+ * @public
+ * @param {number} y
+ * @param {number} height
+ * @param {string} [textVerticalAlign='top']
+ * @return {number} Adjusted y.
+ */
+
+
+function adjustTextY(y, height, textVerticalAlign) {
+  if (textVerticalAlign === 'middle') {
+    y -= height / 2;
+  } else if (textVerticalAlign === 'bottom') {
+    y -= height;
+  }
+
+  return y;
+}
+/**
+ * Follow same interface to `Displayable.prototype.calculateTextPosition`.
+ * @public
+ * @param {Obejct} [out] Prepared out object. If not input, auto created in the method.
+ * @param {module:zrender/graphic/Style} style where `textPosition` and `textDistance` are visited.
+ * @param {Object} rect {x, y, width, height} Rect of the host elment, according to which the text positioned.
+ * @return {Object} The input `out`. Set: {x, y, textAlign, textVerticalAlign}
+ */
+
+
+function calculateTextPosition(out, style, rect) {
+  var textPosition = style.textPosition;
+  var distance = style.textDistance;
+  var x = rect.x;
+  var y = rect.y;
+  distance = distance || 0;
+  var height = rect.height;
+  var width = rect.width;
+  var halfHeight = height / 2;
+  var textAlign = 'left';
+  var textVerticalAlign = 'top';
+
+  switch (textPosition) {
+    case 'left':
+      x -= distance;
+      y += halfHeight;
+      textAlign = 'right';
+      textVerticalAlign = 'middle';
+      break;
+
+    case 'right':
+      x += distance + width;
+      y += halfHeight;
+      textVerticalAlign = 'middle';
+      break;
+
+    case 'top':
+      x += width / 2;
+      y -= distance;
+      textAlign = 'center';
+      textVerticalAlign = 'bottom';
+      break;
+
+    case 'bottom':
+      x += width / 2;
+      y += height + distance;
+      textAlign = 'center';
+      break;
+
+    case 'inside':
+      x += width / 2;
+      y += halfHeight;
+      textAlign = 'center';
+      textVerticalAlign = 'middle';
+      break;
+
+    case 'insideLeft':
+      x += distance;
+      y += halfHeight;
+      textVerticalAlign = 'middle';
+      break;
+
+    case 'insideRight':
+      x += width - distance;
+      y += halfHeight;
+      textAlign = 'right';
+      textVerticalAlign = 'middle';
+      break;
+
+    case 'insideTop':
+      x += width / 2;
+      y += distance;
+      textAlign = 'center';
+      break;
+
+    case 'insideBottom':
+      x += width / 2;
+      y += height - distance;
+      textAlign = 'center';
+      textVerticalAlign = 'bottom';
+      break;
+
+    case 'insideTopLeft':
+      x += distance;
+      y += distance;
+      break;
+
+    case 'insideTopRight':
+      x += width - distance;
+      y += distance;
+      textAlign = 'right';
+      break;
+
+    case 'insideBottomLeft':
+      x += distance;
+      y += height - distance;
+      textVerticalAlign = 'bottom';
+      break;
+
+    case 'insideBottomRight':
+      x += width - distance;
+      y += height - distance;
+      textAlign = 'right';
+      textVerticalAlign = 'bottom';
+      break;
+  }
+
+  out = out || {};
+  out.x = x;
+  out.y = y;
+  out.textAlign = textAlign;
+  out.textVerticalAlign = textVerticalAlign;
+  return out;
+}
+/**
+ * To be removed. But still do not remove in case that some one has imported it.
+ * @deprecated
+ * @public
+ * @param {stirng} textPosition
+ * @param {Object} rect {x, y, width, height}
+ * @param {number} distance
+ * @return {Object} {x, y, textAlign, textVerticalAlign}
+ */
+
+
+function adjustTextPositionOnRect(textPosition, rect, distance) {
+  var dummyStyle = {
+    textPosition: textPosition,
+    textDistance: distance
+  };
+  return calculateTextPosition({}, dummyStyle, rect);
+}
+/**
+ * Show ellipsis if overflow.
+ *
+ * @public
+ * @param  {string} text
+ * @param  {string} containerWidth
+ * @param  {string} font
+ * @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, font, ellipsis, options) {
+  if (!containerWidth) {
+    return '';
+  }
+
+  var textLines = (text + '').split('\n');
+  options = prepareTruncateOptions(containerWidth, font, ellipsis, options); // FIXME
+  // It is not appropriate that every line has '...' when truncate multiple lines.
+
+  for (var i = 0, len = textLines.length; i < len; i++) {
+    textLines[i] = truncateSingleLine(textLines[i], options);
+  }
+
+  return textLines.join('\n');
+}
+
+function prepareTruncateOptions(containerWidth, font, ellipsis, options) {
+  options = extend({}, options);
+  options.font = font;
+  var ellipsis = retrieve2(ellipsis, '...');
+  options.maxIterations = retrieve2(options.maxIterations, 2);
+  var minChar = options.minChar = retrieve2(options.minChar, 0); // FIXME
+  // Other languages?
+
+  options.cnCharWidth = getWidth('国', font); // FIXME
+  // Consider proportional font?
+
+  var ascCharWidth = options.ascCharWidth = getWidth('a', font);
+  options.placeholder = retrieve2(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 = getWidth(ellipsis, font);
+
+  if (ellipsisWidth > contentWidth) {
+    ellipsis = '';
+    ellipsisWidth = 0;
+  }
+
+  contentWidth = containerWidth - ellipsisWidth;
+  options.ellipsis = ellipsis;
+  options.ellipsisWidth = ellipsisWidth;
+  options.contentWidth = contentWidth;
+  options.containerWidth = containerWidth;
+  return options;
+}
+
+function truncateSingleLine(textLine, options) {
+  var containerWidth = options.containerWidth;
+  var font = options.font;
+  var contentWidth = options.contentWidth;
+
+  if (!containerWidth) {
+    return '';
+  }
+
+  var lineWidth = getWidth(textLine, font);
+
+  if (lineWidth <= containerWidth) {
+    return textLine;
+  }
+
+  for (var j = 0;; j++) {
+    if (lineWidth <= contentWidth || j >= options.maxIterations) {
+      textLine += options.ellipsis;
+      break;
+    }
+
+    var subLength = j === 0 ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth) : lineWidth > 0 ? Math.floor(textLine.length * contentWidth / lineWidth) : 0;
+    textLine = textLine.substr(0, subLength);
+    lineWidth = getWidth(textLine, font);
+  }
+
+  if (textLine === '') {
+    textLine = options.placeholder;
+  }
+
+  return textLine;
+}
+
+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;
+}
+/**
+ * @public
+ * @param {string} font
+ * @return {number} line height
+ */
+
+
+function getLineHeight(font) {
+  // FIXME A rough approach.
+  return getWidth('国', font);
+}
+/**
+ * @public
+ * @param {string} text
+ * @param {string} font
+ * @return {Object} width
+ */
+
+
+function measureText(text, font) {
+  return methods.measureText(text, font);
+} // Avoid assign to an exported variable, for transforming to cjs.
+
+
+methods.measureText = function (text, font) {
+  var ctx = getContext();
+  ctx.font = font || DEFAULT_FONT;
+  return ctx.measureText(text);
+};
+/**
+ * @public
+ * @param {string} text
+ * @param {string} font
+ * @param {Object} [truncate]
+ * @return {Object} block: {lineHeight, lines, height, outerHeight, canCacheByTextString}
+ *  Notice: for performance, do not calculate outerWidth util needed.
+ *  `canCacheByTextString` means the result `lines` is only determined by the input `text`.
+ *  Thus we can simply comparing the `input` text to determin whether the result changed,
+ *  without travel the result `lines`.
+ */
+
+
+function parsePlainText(text, font, padding, textLineHeight, truncate) {
+  text != null && (text += '');
+  var lineHeight = retrieve2(textLineHeight, getLineHeight(font));
+  var lines = text ? text.split('\n') : [];
+  var height = lines.length * lineHeight;
+  var outerHeight = height;
+  var canCacheByTextString = true;
+
+  if (padding) {
+    outerHeight += padding[0] + padding[2];
+  }
+
+  if (text && truncate) {
+    canCacheByTextString = false;
+    var truncOuterHeight = truncate.outerHeight;
+    var truncOuterWidth = truncate.outerWidth;
+
+    if (truncOuterHeight != null && outerHeight > truncOuterHeight) {
+      text = '';
+      lines = [];
+    } else if (truncOuterWidth != null) {
+      var options = prepareTruncateOptions(truncOuterWidth - (padding ? padding[1] + padding[3] : 0), font, truncate.ellipsis, {
+        minChar: truncate.minChar,
+        placeholder: truncate.placeholder
+      }); // FIXME
+      // It is not appropriate that every line has '...' when truncate multiple lines.
+
+      for (var i = 0, len = lines.length; i < len; i++) {
+        lines[i] = truncateSingleLine(lines[i], options);
+      }
+    }
+  }
+
+  return {
+    lines: lines,
+    height: height,
+    outerHeight: outerHeight,
+    lineHeight: lineHeight,
+    canCacheByTextString: canCacheByTextString
+  };
+}
+/**
+ * For example: 'some text {a|some text}other text{b|some text}xxx{c|}xxx'
+ * Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
+ *
+ * @public
+ * @param {string} text
+ * @param {Object} style
+ * @return {Object} block
+ * {
+ *      width,
+ *      height,
+ *      lines: [{
+ *          lineHeight,
+ *          width,
+ *          tokens: [[{
+ *              styleName,
+ *              text,
+ *              width,      // include textPadding
+ *              height,     // include textPadding
+ *              textWidth, // pure text width
+ *              textHeight, // pure text height
+ *              lineHeihgt,
+ *              font,
+ *              textAlign,
+ *              textVerticalAlign
+ *          }], [...], ...]
+ *      }, ...]
+ * }
+ * If styleName is undefined, it is plain text.
+ */
+
+
+function parseRichText(text, style) {
+  var contentBlock = {
+    lines: [],
+    width: 0,
+    height: 0
+  };
+  text != null && (text += '');
+
+  if (!text) {
+    return contentBlock;
+  }
+
+  var lastIndex = STYLE_REG.lastIndex = 0;
+  var result;
+
+  while ((result = STYLE_REG.exec(text)) != null) {
+    var matchedIndex = result.index;
+
+    if (matchedIndex > lastIndex) {
+      pushTokens(contentBlock, text.substring(lastIndex, matchedIndex));
+    }
+
+    pushTokens(contentBlock, result[2], result[1]);
+    lastIndex = STYLE_REG.lastIndex;
+  }
+
+  if (lastIndex < text.length) {
+    pushTokens(contentBlock, text.substring(lastIndex, text.length));
+  }
+
+  var lines = contentBlock.lines;
+  var contentHeight = 0;
+  var contentWidth = 0; // For `textWidth: 100%`
+
+  var pendingList = [];
+  var stlPadding = style.textPadding;
+  var truncate = style.truncate;
+  var truncateWidth = truncate && truncate.outerWidth;
+  var truncateHeight = truncate && truncate.outerHeight;
+
+  if (stlPadding) {
+    truncateWidth != null && (truncateWidth -= stlPadding[1] + stlPadding[3]);
+    truncateHeight != null && (truncateHeight -= stlPadding[0] + stlPadding[2]);
+  } // Calculate layout info of tokens.
+
+
+  for (var i = 0; i < lines.length; i++) {
+    var line = lines[i];
+    var lineHeight = 0;
+    var lineWidth = 0;
+
+    for (var j = 0; j < line.tokens.length; j++) {
+      var token = line.tokens[j];
+      var tokenStyle = token.styleName && style.rich[token.styleName] || {}; // textPadding should not inherit from style.
+
+      var textPadding = token.textPadding = tokenStyle.textPadding; // textFont has been asigned to font by `normalizeStyle`.
+
+      var font = token.font = tokenStyle.font || style.font; // textHeight can be used when textVerticalAlign is specified in token.
+
+      var tokenHeight = token.textHeight = retrieve2( // textHeight should not be inherited, consider it can be specified
+      // as box height of the block.
+      tokenStyle.textHeight, getLineHeight(font));
+      textPadding && (tokenHeight += textPadding[0] + textPadding[2]);
+      token.height = tokenHeight;
+      token.lineHeight = retrieve3(tokenStyle.textLineHeight, style.textLineHeight, tokenHeight);
+      token.textAlign = tokenStyle && tokenStyle.textAlign || style.textAlign;
+      token.textVerticalAlign = tokenStyle && tokenStyle.textVerticalAlign || 'middle';
+
+      if (truncateHeight != null && contentHeight + token.lineHeight > truncateHeight) {
+        return {
+          lines: [],
+          width: 0,
+          height: 0
+        };
+      }
+
+      token.textWidth = getWidth(token.text, font);
+      var tokenWidth = tokenStyle.textWidth;
+      var tokenWidthNotSpecified = tokenWidth == null || tokenWidth === 'auto'; // Percent width, can be `100%`, can be used in drawing separate
+      // line when box width is needed to be auto.
+
+      if (typeof tokenWidth === 'string' && tokenWidth.charAt(tokenWidth.length - 1) === '%') {
+        token.percentWidth = tokenWidth;
+        pendingList.push(token);
+        tokenWidth = 0; // Do not truncate in this case, because there is no user case
+        // and it is too complicated.
+      } else {
+        if (tokenWidthNotSpecified) {
+          tokenWidth = token.textWidth; // FIXME: If image is not loaded and textWidth is not specified, calling
+          // `getBoundingRect()` will not get correct result.
+
+          var textBackgroundColor = tokenStyle.textBackgroundColor;
+          var bgImg = textBackgroundColor && textBackgroundColor.image; // Use cases:
+          // (1) If image is not loaded, it will be loaded at render phase and call
+          // `dirty()` and `textBackgroundColor.image` will be replaced with the loaded
+          // image, and then the right size will be calculated here at the next tick.
+          // See `graphic/helper/text.js`.
+          // (2) If image loaded, and `textBackgroundColor.image` is image src string,
+          // use `imageHelper.findExistImage` to find cached image.
+          // `imageHelper.findExistImage` will always be called here before
+          // `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`
+          // which ensures that image will not be rendered before correct size calcualted.
+
+          if (bgImg) {
+            bgImg = imageHelper.findExistImage(bgImg);
+
+            if (imageHelper.isImageReady(bgImg)) {
+              tokenWidth = Math.max(tokenWidth, bgImg.width * tokenHeight / bgImg.height);
+            }
+          }
+        }
+
+        var paddingW = textPadding ? textPadding[1] + textPadding[3] : 0;
+        tokenWidth += paddingW;
+        var remianTruncWidth = truncateWidth != null ? truncateWidth - lineWidth : null;
+
+        if (remianTruncWidth != null && remianTruncWidth < tokenWidth) {
+          if (!tokenWidthNotSpecified || remianTruncWidth < paddingW) {
+            token.text = '';
+            token.textWidth = tokenWidth = 0;
+          } else {
+            token.text = truncateText(token.text, remianTruncWidth - paddingW, font, truncate.ellipsis, {
+              minChar: truncate.minChar
+            });
+            token.textWidth = getWidth(token.text, font);
+            tokenWidth = token.textWidth + paddingW;
+          }
+        }
+      }
+
+      lineWidth += token.width = tokenWidth;
+      tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight));
+    }
+
+    line.width = lineWidth;
+    line.lineHeight = lineHeight;
+    contentHeight += lineHeight;
+    contentWidth = Math.max(contentWidth, lineWidth);
+  }
+
+  contentBlock.outerWidth = contentBlock.width = retrieve2(style.textWidth, contentWidth);
+  contentBlock.outerHeight = contentBlock.height = retrieve2(style.textHeight, contentHeight);
+
+  if (stlPadding) {
+    contentBlock.outerWidth += stlPadding[1] + stlPadding[3];
+    contentBlock.outerHeight += stlPadding[0] + stlPadding[2];
+  }
+
+  for (var i = 0; i < pendingList.length; i++) {
+    var token = pendingList[i];
+    var percentWidth = token.percentWidth; // Should not base on outerWidth, because token can not be placed out of padding.
+
+    token.width = parseInt(percentWidth, 10) / 100 * contentWidth;
+  }
+
+  return contentBlock;
+}
+
+function pushTokens(block, str, styleName) {
+  var isEmptyStr = str === '';
+  var strs = str.split('\n');
+  var lines = block.lines;
+
+  for (var i = 0; i < strs.length; i++) {
+    var text = strs[i];
+    var token = {
+      styleName: styleName,
+      text: text,
+      isLineHolder: !text && !isEmptyStr
+    }; // The first token should be appended to the last line.
+
+    if (!i) {
+      var tokens = (lines[lines.length - 1] || (lines[0] = {
+        tokens: []
+      })).tokens; // Consider cases:
+      // (1) ''.split('\n') => ['', '\n', ''], the '' at the first item
+      // (which is a placeholder) should be replaced by new token.
+      // (2) A image backage, where token likes {a|}.
+      // (3) A redundant '' will affect textAlign in line.
+      // (4) tokens with the same tplName should not be merged, because
+      // they should be displayed in different box (with border and padding).
+
+      var tokensLen = tokens.length;
+      tokensLen === 1 && tokens[0].isLineHolder ? tokens[0] = token : // Consider text is '', only insert when it is the "lineHolder" or
+      // "emptyStr". Otherwise a redundant '' will affect textAlign in line.
+      (text || !tokensLen || isEmptyStr) && tokens.push(token);
+    } // Other tokens always start a new line.
+    else {
+        // If there is '', insert it as a placeholder.
+        lines.push({
+          tokens: [token]
+        });
+      }
+  }
+}
+
+function makeFont(style) {
+  // FIXME in node-canvas fontWeight is before fontStyle
+  // Use `fontSize` `fontFamily` to check whether font properties are defined.
+  var font = (style.fontSize || style.fontFamily) && [style.fontStyle, style.fontWeight, (style.fontSize || 12) + 'px', // If font properties are defined, `fontFamily` should not be ignored.
+  style.fontFamily || 'sans-serif'].join(' ');
+  return font && trim(font) || style.textFont || style.font;
+}
+
+exports.DEFAULT_FONT = DEFAULT_FONT;
+exports.$override = $override;
+exports.getWidth = getWidth;
+exports.getBoundingRect = getBoundingRect;
+exports.adjustTextX = adjustTextX;
+exports.adjustTextY = adjustTextY;
+exports.calculateTextPosition = calculateTextPosition;
+exports.adjustTextPositionOnRect = adjustTextPositionOnRect;
+exports.truncateText = truncateText;
+exports.getLineHeight = getLineHeight;
+exports.measureText = measureText;
+exports.parsePlainText = parsePlainText;
+exports.parseRichText = parseRichText;
+exports.makeFont = makeFont;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/util.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/util.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+var PI2 = Math.PI * 2;
+
+function normalizeRadian(angle) {
+  angle %= PI2;
+
+  if (angle < 0) {
+    angle += PI2;
+  }
+
+  return angle;
+}
+
+exports.normalizeRadian = normalizeRadian;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/contain/windingLine.js":
+/*!*********************************************************!*\
+  !*** ./node_modules/zrender/lib/contain/windingLine.js ***!
+  \*********************************************************/
+/*! no static exports found */
+/***/ (function(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; // If (x, y) on the line, considered as "contain".
+
+  return x_ === x ? Infinity : x_ > x ? dir : 0;
+}
+
+module.exports = windingLine;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/container/Group.js":
+/*!*****************************************************!*\
+  !*** ./node_modules/zrender/lib/container/Group.js ***!
+  \*****************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var Element = __webpack_require__(/*! ../Element */ "./node_modules/zrender/lib/Element.js");
+
+var BoundingRect = __webpack_require__(/*! ../core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+/**
+ * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上
+ * @module zrender/graphic/Group
+ * @example
+ *     var Group = require('zrender/container/Group');
+ *     var Circle = require('zrender/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);
+ */
+
+/**
+ * @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);
+var _default = Group;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/BoundingRect.js":
+/*!*******************************************************!*\
+  !*** ./node_modules/zrender/lib/core/BoundingRect.js ***!
+  \*******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var vec2 = __webpack_require__(/*! ./vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var matrix = __webpack_require__(/*! ./matrix */ "./node_modules/zrender/lib/core/matrix.js");
+
+/**
+ * @module echarts/core/BoundingRect
+ */
+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);
+};
+
+var _default = BoundingRect;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/LRU.js":
+/*!**********************************************!*\
+  !*** ./node_modules/zrender/lib/core/LRU.js ***!
+  \**********************************************/
+/*! no static exports found */
+/***/ (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 = {};
+};
+
+var _default = LRU;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/PathProxy.js":
+/*!****************************************************!*\
+  !*** ./node_modules/zrender/lib/core/PathProxy.js ***!
+  \****************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var curve = __webpack_require__(/*! ./curve */ "./node_modules/zrender/lib/core/curve.js");
+
+var vec2 = __webpack_require__(/*! ./vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var bbox = __webpack_require__(/*! ./bbox */ "./node_modules/zrender/lib/core/bbox.js");
+
+var BoundingRect = __webpack_require__(/*! ./BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+var _config = __webpack_require__(/*! ../config */ "./node_modules/zrender/lib/config.js");
+
+var dpr = _config.devicePixelRatio;
+
+/**
+ * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
+ * 可以用于 isInsidePath 判断以及获取boundingRect
+ *
+ * @module zrender/core/PathProxy
+ * @author Yi Shen (http://www.github.com/pissang)
+ */
+// TODO getTotalLength, getPointAtLength
+
+/* global Float32Array */
+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, segmentIgnoreThreshold) {
+    // Compat. Previously there is no segmentIgnoreThreshold.
+    segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
+    this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
+    this._uy = mathAbs(segmentIgnoreThreshold / 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 + cy;
+    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 旋转
+
+          i += 1;
+          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 {CanvasRenderingContext2D} ctx
+   */
+  rebuildPath: function (ctx) {
+    var d = this.data;
+    var x0;
+    var y0;
+    var xi;
+    var yi;
+    var x;
+    var 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;
+var _default = PathProxy;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/bbox.js":
+/*!***********************************************!*\
+  !*** ./node_modules/zrender/lib/core/bbox.js ***!
+  \***********************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var vec2 = __webpack_require__(/*! ./vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var curve = __webpack_require__(/*! ./curve */ "./node_modules/zrender/lib/core/curve.js");
+
+/**
+ * @author Yi Shen(https://github.com/pissang)
+ */
+var mathMin = Math.min;
+var mathMax = Math.max;
+var mathSin = Math.sin;
+var mathCos = Math.cos;
+var PI2 = Math.PI * 2;
+var start = vec2.create();
+var end = vec2.create();
+var extremity = vec2.create();
+/**
+ * 从顶点数组中计算出最小包围盒,写入`min`和`max`中
+ * @module zrender/core/bbox
+ * @param {Array<Object>} points 顶点数组
+ * @param {number} min
+ * @param {number} max
+ */
+
+function fromPoints(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
+ */
+
+
+function fromLine(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
+ */
+
+function fromCubic(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
+ */
+
+
+function fromQuadratic(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
+ */
+
+
+function fromArc(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);
+    }
+  }
+}
+
+exports.fromPoints = fromPoints;
+exports.fromLine = fromLine;
+exports.fromCubic = fromCubic;
+exports.fromQuadratic = fromQuadratic;
+exports.fromArc = fromArc;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/curve.js":
+/*!************************************************!*\
+  !*** ./node_modules/zrender/lib/core/curve.js ***!
+  \************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _vector = __webpack_require__(/*! ./vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var v2Create = _vector.create;
+var v2DistSquare = _vector.distSquare;
+
+/**
+ * 曲线辅助模块
+ * @module zrender/core/curve
+ * @author pissang(https://www.github.com/pissang)
+ */
+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();
+
+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);
+}
+
+exports.cubicAt = cubicAt;
+exports.cubicDerivativeAt = cubicDerivativeAt;
+exports.cubicRootAt = cubicRootAt;
+exports.cubicExtrema = cubicExtrema;
+exports.cubicSubdivide = cubicSubdivide;
+exports.cubicProjectPoint = cubicProjectPoint;
+exports.quadraticAt = quadraticAt;
+exports.quadraticDerivativeAt = quadraticDerivativeAt;
+exports.quadraticRootAt = quadraticRootAt;
+exports.quadraticExtremum = quadraticExtremum;
+exports.quadraticSubdivide = quadraticSubdivide;
+exports.quadraticProjectPoint = quadraticProjectPoint;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/env.js":
+/*!**********************************************!*\
+  !*** ./node_modules/zrender/lib/core/env.js ***!
+  \**********************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * echarts设备环境识别
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author firede[firede@firede.us]
+ * @desc thanks zepto.
+ */
+
+/* global wx */
+var env = {};
+
+if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {
+  // In Weixin Application
+  env = {
+    browser: {},
+    os: {},
+    node: false,
+    wxa: true,
+    // Weixin Application
+    canvasSupported: true,
+    svgSupported: false,
+    touchEventsSupported: true,
+    domSupported: false
+  };
+} else if (typeof document === 'undefined' && typeof self !== 'undefined') {
+  // In worker
+  env = {
+    browser: {},
+    os: {},
+    node: false,
+    worker: true,
+    canvasSupported: true,
+    domSupported: false
+  };
+} else if (typeof navigator === 'undefined') {
+  // In node
+  env = {
+    browser: {},
+    os: {},
+    node: true,
+    worker: false,
+    // Assume canvas is supported
+    canvasSupported: true,
+    svgSupported: true,
+    domSupported: false
+  };
+} else {
+  env = detect(navigator.userAgent);
+}
+
+var _default = 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,
+    svgSupported: typeof SVGRect !== 'undefined',
+    // 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: // (1) 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.
+    // For example, in iOS 13 Mobile Chromium 78, if the touching behavior starts page
+    // scroll, the `pointermove` event can not be fired any more. That will break some
+    // features like "pan horizontally to move something and pan vertically to page scroll".
+    // The horizontal pan probably be interrupted by the casually triggered page scroll.
+    // (2) 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)
+    'onpointerdown' in window && (browser.edge || browser.ie && browser.version >= 11),
+    // passiveSupported: detectPassiveSupport()
+    domSupported: typeof document !== 'undefined'
+  };
+} // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
+// function detectPassiveSupport() {
+//     // Test via a getter in the options object to see if the passive property is accessed
+//     var supportsPassive = false;
+//     try {
+//         var opts = Object.defineProperty({}, 'passive', {
+//             get: function() {
+//                 supportsPassive = true;
+//             }
+//         });
+//         window.addEventListener('testPassive', function() {}, opts);
+//     } catch (e) {
+//     }
+//     return supportsPassive;
+// }
+
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/guid.js":
+/*!***********************************************!*\
+  !*** ./node_modules/zrender/lib/core/guid.js ***!
+  \***********************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * zrender: 生成唯一id
+ *
+ * @author errorrik (errorrik@gmail.com)
+ */
+var idStart = 0x0907;
+
+function _default() {
+  return idStart++;
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/log.js":
+/*!**********************************************!*\
+  !*** ./node_modules/zrender/lib/core/log.js ***!
+  \**********************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _config = __webpack_require__(/*! ../config */ "./node_modules/zrender/lib/config.js");
+
+var debugMode = _config.debugMode;
+
+var logError = function () {};
+
+if (debugMode === 1) {
+  logError = console.error;
+}
+
+var _default = logError;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/matrix.js":
+/*!*************************************************!*\
+  !*** ./node_modules/zrender/lib/core/matrix.js ***!
+  \*************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * 3x2矩阵操作类
+ * @exports zrender/tool/matrix
+ */
+
+/* global Float32Array */
+var ArrayCtor = typeof Float32Array === 'undefined' ? Array : Float32Array;
+/**
+ * Create a identity matrix.
+ * @return {Float32Array|Array.<number>}
+ */
+
+function create() {
+  var out = new ArrayCtor(6);
+  identity(out);
+  return out;
+}
+/**
+ * 设置矩阵为单位矩阵
+ * @param {Float32Array|Array.<number>} out
+ */
+
+
+function identity(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
+ */
+
+
+function copy(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
+ */
+
+
+function mul(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
+ */
+
+
+function translate(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
+ */
+
+
+function rotate(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
+ */
+
+
+function scale(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
+ */
+
+
+function invert(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;
+}
+/**
+ * Clone a new matrix.
+ * @param {Float32Array|Array.<number>} a
+ */
+
+
+function clone(a) {
+  var b = create();
+  copy(b, a);
+  return b;
+}
+
+exports.create = create;
+exports.identity = identity;
+exports.copy = copy;
+exports.mul = mul;
+exports.translate = translate;
+exports.rotate = rotate;
+exports.scale = scale;
+exports.invert = invert;
+exports.clone = clone;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/util.js":
+/*!***********************************************!*\
+  !*** ./node_modules/zrender/lib/core/util.js ***!
+  \***********************************************/
+/*! no static exports found */
+/***/ (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; // Avoid assign to an exported variable, for transforming to cjs.
+
+var methods = {};
+
+function $override(name, fn) {
+  // Clear ctx instance for different environment
+  if (name === 'createCanvas') {
+    _ctx = null;
+  }
+
+  methods[name] = fn;
+}
+/**
+ * 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]') {
+    if (!isPrimitive(source)) {
+      result = [];
+
+      for (var i = 0, len = source.length; i < len; i++) {
+        result[i] = clone(source[i]);
+      }
+    }
+  } else if (TYPED_ARRAY[typeStr]) {
+    if (!isPrimitive(source)) {
+      var Ctor = source.constructor;
+
+      if (source.constructor.from) {
+        result = Ctor.from(source);
+      } else {
+        result = new Ctor(source.length);
+
+        for (var i = 0, len = source.length; i < len; i++) {
+          result[i] = clone(source[i]);
+        }
+      }
+    }
+  } 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 {boolean} [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;
+}
+
+var createCanvas = function () {
+  return methods.createCanvas();
+};
+
+methods.createCanvas = function () {
+  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 = 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) {
+    if (clazzPrototype.hasOwnProperty(prop)) {
+      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 {*}
+ */
+
+
+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 isTypedArray(value) {
+  return !!TYPED_ARRAY[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) {
+  /* eslint-disable-next-line no-self-compare */
+  return value !== value;
+}
+/**
+ * If value1 is not null, then return value1, otherwise judget rest of values.
+ * Low performance.
+ * @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];
+    }
+  }
+}
+
+function retrieve2(value0, value1) {
+  return value0 != null ? value0 : value1;
+}
+
+function retrieve3(value0, value1, value2) {
+  return value0 != null ? value0 : value1 != null ? value1 : value2;
+}
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Array} arr
+ * @param {number} startIndex
+ * @param {number} endIndex
+ * @return {Array}
+ */
+
+
+function slice() {
+  return Function.call.apply(nativeSlice, arguments);
+}
+/**
+ * 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
+ * @return {Array.<number>}
+ */
+
+
+function normalizeCssArray(val) {
+  if (typeof val === 'number') {
+    return [val, val, val, val];
+  }
+
+  var len = val.length;
+
+  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;
+}
+/**
+ * @memberOf module:zrender/core/util
+ * @param {boolean} condition
+ * @param {string} message
+ */
+
+
+function assert(condition, message) {
+  if (!condition) {
+    throw new Error(message);
+  }
+}
+/**
+ * @memberOf module:zrender/core/util
+ * @param {string} str string to be trimed
+ * @return {string} trimed string
+ */
+
+
+function trim(str) {
+  if (str == null) {
+    return null;
+  } else if (typeof str.trim === 'function') {
+    return str.trim();
+  } else {
+    return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
+  }
+}
+
+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
+ * @param {Object} obj Only apply `ownProperty`.
+ */
+
+
+function HashMap(obj) {
+  var isArr = isArray(obj); // Key should not be set on this, otherwise
+  // methods get/set/... may be overrided.
+
+  this.data = {};
+  var thisMap = this;
+  obj instanceof HashMap ? obj.each(visit) : obj && each(obj, visit);
+
+  function visit(value, key) {
+    isArr ? thisMap.set(value, key) : thisMap.set(key, value);
+  }
+}
+
+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.data.hasOwnProperty(key) ? this.data[key] : null;
+  },
+  set: function (key, value) {
+    // Comparing with invocation chaining, `return value` is more commonly
+    // used in this case: `var someVal = map.set('a', genVal());`
+    return this.data[key] = 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));
+    /* eslint-disable guard-for-in */
+
+    for (var key in this.data) {
+      this.data.hasOwnProperty(key) && cb(this.data[key], key);
+    }
+    /* eslint-enable guard-for-in */
+
+  },
+  // Do not use this method if performance sensitive.
+  removeKey: function (key) {
+    delete this.data[key];
+  }
+};
+
+function createHashMap(obj) {
+  return new HashMap(obj);
+}
+
+function concatArray(a, b) {
+  var newArray = new a.constructor(a.length + b.length);
+
+  for (var i = 0; i < a.length; i++) {
+    newArray[i] = a[i];
+  }
+
+  var offset = a.length;
+
+  for (i = 0; i < b.length; i++) {
+    newArray[i + offset] = b[i];
+  }
+
+  return newArray;
+}
+
+function noop() {}
+
+exports.$override = $override;
+exports.clone = clone;
+exports.merge = merge;
+exports.mergeAll = mergeAll;
+exports.extend = extend;
+exports.defaults = defaults;
+exports.createCanvas = createCanvas;
+exports.getContext = getContext;
+exports.indexOf = indexOf;
+exports.inherits = inherits;
+exports.mixin = mixin;
+exports.isArrayLike = isArrayLike;
+exports.each = each;
+exports.map = map;
+exports.reduce = reduce;
+exports.filter = filter;
+exports.find = find;
+exports.bind = bind;
+exports.curry = curry;
+exports.isArray = isArray;
+exports.isFunction = isFunction;
+exports.isString = isString;
+exports.isObject = isObject;
+exports.isBuiltInObject = isBuiltInObject;
+exports.isTypedArray = isTypedArray;
+exports.isDom = isDom;
+exports.eqNaN = eqNaN;
+exports.retrieve = retrieve;
+exports.retrieve2 = retrieve2;
+exports.retrieve3 = retrieve3;
+exports.slice = slice;
+exports.normalizeCssArray = normalizeCssArray;
+exports.assert = assert;
+exports.trim = trim;
+exports.setAsPrimitive = setAsPrimitive;
+exports.isPrimitive = isPrimitive;
+exports.createHashMap = createHashMap;
+exports.concatArray = concatArray;
+exports.noop = noop;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/core/vector.js":
+/*!*************************************************!*\
+  !*** ./node_modules/zrender/lib/core/vector.js ***!
+  \*************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/* global Float32Array */
+var ArrayCtor = typeof Float32Array === 'undefined' ? Array : Float32Array;
+/**
+ * 创建一个向量
+ * @param {number} [x=0]
+ * @param {number} [y=0]
+ * @return {Vector2}
+ */
+
+function create(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}
+ */
+
+
+function copy(out, v) {
+  out[0] = v[0];
+  out[1] = v[1];
+  return out;
+}
+/**
+ * 克隆一个向量
+ * @param {Vector2} v
+ * @return {Vector2}
+ */
+
+
+function clone(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} 结果
+ */
+
+
+function set(out, a, b) {
+  out[0] = a;
+  out[1] = b;
+  return out;
+}
+/**
+ * 向量相加
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+
+
+function add(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
+ */
+
+
+function scaleAndAdd(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
+ */
+
+
+function sub(out, v1, v2) {
+  out[0] = v1[0] - v2[0];
+  out[1] = v1[1] - v2[1];
+  return out;
+}
+/**
+ * 向量长度
+ * @param {Vector2} v
+ * @return {number}
+ */
+
+
+function len(v) {
+  return Math.sqrt(lenSquare(v));
+}
+
+var length = len; // jshint ignore:line
+
+/**
+ * 向量长度平方
+ * @param {Vector2} v
+ * @return {number}
+ */
+
+function lenSquare(v) {
+  return v[0] * v[0] + v[1] * v[1];
+}
+
+var lengthSquare = lenSquare;
+/**
+ * 向量乘法
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+
+function mul(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
+ */
+
+
+function div(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}
+ */
+
+
+function dot(v1, v2) {
+  return v1[0] * v2[0] + v1[1] * v2[1];
+}
+/**
+ * 向量缩放
+ * @param {Vector2} out
+ * @param {Vector2} v
+ * @param {number} s
+ */
+
+
+function scale(out, v, s) {
+  out[0] = v[0] * s;
+  out[1] = v[1] * s;
+  return out;
+}
+/**
+ * 向量归一化
+ * @param {Vector2} out
+ * @param {Vector2} v
+ */
+
+
+function normalize(out, v) {
+  var d = 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}
+ */
+
+
+function distance(v1, v2) {
+  return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]));
+}
+
+var dist = distance;
+/**
+ * 向量距离平方
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @return {number}
+ */
+
+function distanceSquare(v1, v2) {
+  return (v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]);
+}
+
+var distSquare = distanceSquare;
+/**
+ * 求负向量
+ * @param {Vector2} out
+ * @param {Vector2} v
+ */
+
+function negate(out, v) {
+  out[0] = -v[0];
+  out[1] = -v[1];
+  return out;
+}
+/**
+ * 插值两个点
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @param {number} t
+ */
+
+
+function lerp(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
+ */
+
+
+function applyTransform(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
+ */
+
+
+function min(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
+ */
+
+
+function max(out, v1, v2) {
+  out[0] = Math.max(v1[0], v2[0]);
+  out[1] = Math.max(v1[1], v2[1]);
+  return out;
+}
+
+exports.create = create;
+exports.copy = copy;
+exports.clone = clone;
+exports.set = set;
+exports.add = add;
+exports.scaleAndAdd = scaleAndAdd;
+exports.sub = sub;
+exports.len = len;
+exports.length = length;
+exports.lenSquare = lenSquare;
+exports.lengthSquare = lengthSquare;
+exports.mul = mul;
+exports.div = div;
+exports.dot = dot;
+exports.scale = scale;
+exports.normalize = normalize;
+exports.distance = distance;
+exports.dist = dist;
+exports.distanceSquare = distanceSquare;
+exports.distSquare = distSquare;
+exports.negate = negate;
+exports.lerp = lerp;
+exports.applyTransform = applyTransform;
+exports.min = min;
+exports.max = max;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/CompoundPath.js":
+/*!**********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/CompoundPath.js ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ./Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+// CompoundPath to improve performance
+var _default = 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], paths[i].segmentIgnoreThreshold);
+    }
+  },
+  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);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Displayable.js":
+/*!*********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Displayable.js ***!
+  \*********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var Style = __webpack_require__(/*! ./Style */ "./node_modules/zrender/lib/graphic/Style.js");
+
+var Element = __webpack_require__(/*! ../Element */ "./node_modules/zrender/lib/Element.js");
+
+var RectText = __webpack_require__(/*! ./mixin/RectText */ "./node_modules/zrender/lib/graphic/mixin/RectText.js");
+
+/**
+ * Base class of all displayable graphic objects
+ * @module zrender/graphic/Displayable
+ */
+
+/**
+ * @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);
+  this._rect = null; // Shapes for cascade clipping.
+  // Can only be `null`/`undefined` or an non-empty array, MUST NOT be an empty array.
+  // because it is easy to only using null to check whether clipPaths changed.
+
+  this.__clipPaths = null; // FIXME Stateful must be mixined after style is setted
+  // Stateful.call(this, opts);
+}
+
+Displayable.prototype = {
+  constructor: Displayable,
+  type: 'displayable',
+
+  /**
+   * Dirty flag. From which painter will determine if this displayable object needs brush.
+   * @name module:zrender/graphic/Displayable#__dirty
+   * @type {boolean}
+   */
+  __dirty: true,
+
+  /**
+   * Whether the displayable object is visible. when it is true, the displayable object
+   * is not drawn, but the mouse event can still trigger the object.
+   * @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,
+
+  /**
+   * The z level determines the displayable object can be drawn in which layer canvas.
+   * @name module:/zrender/graphic/Displayable#zlevel
+   * @type {number}
+   * @default 0
+   */
+  zlevel: 0,
+
+  /**
+   * Whether it can be dragged.
+   * @name module:/zrender/graphic/Displayable#draggable
+   * @type {boolean}
+   * @default false
+   */
+  draggable: false,
+
+  /**
+   * Whether is it dragging.
+   * @name module:/zrender/graphic/Displayable#draggable
+   * @type {boolean}
+   * @default false
+   */
+  dragging: false,
+
+  /**
+   * Whether to respond to mouse events.
+   * @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 {boolean}
+   */
+  progressive: false,
+
+  /**
+   * @type {boolean}
+   */
+  incremental: false,
+
+  /**
+   * Scale ratio for global scale.
+   * @type {boolean}
+   */
+  globalScaleRatio: 1,
+  beforeBrush: function (ctx) {},
+  afterBrush: function (ctx) {},
+
+  /**
+   * Graphic drawing method.
+   * @param {CanvasRenderingContext2D} ctx
+   */
+  // Interface
+  brush: function (ctx, prevEl) {},
+
+  /**
+   * Get the minimum bounding box.
+   * @return {module:zrender/core/BoundingRect}
+   */
+  // Interface
+  getBoundingRect: function () {},
+
+  /**
+   * 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);
+  },
+
+  /**
+   * 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 = this.__dirtyText = true;
+    this._rect = null;
+    this.__zr && this.__zr.refresh();
+  },
+
+  /**
+   * If displayable object binded any event
+   * @return {boolean}
+   */
+  // TODO, events bound by 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);
+    this.dirty(false);
+    return this;
+  },
+
+  /**
+   * The string value of `textPosition` needs to be calculated to a real postion.
+   * For example, `'inside'` is calculated to `[rect.width/2, rect.height/2]`
+   * by default. See `contain/text.js#calculateTextPosition` for more details.
+   * But some coutom shapes like "pin", "flag" have center that is not exactly
+   * `[width/2, height/2]`. So we provide this hook to customize the calculation
+   * for those shapes. It will be called if the `style.textPosition` is a string.
+   * @param {Obejct} [out] Prepared out object. If not provided, this method should
+   *        be responsible for creating one.
+   * @param {module:zrender/graphic/Style} style
+   * @param {Object} rect {x, y, width, height}
+   * @return {Obejct} out The same as the input out.
+   *         {
+   *             x: number. mandatory.
+   *             y: number. mandatory.
+   *             textAlign: string. optional. use style.textAlign by default.
+   *             textVerticalAlign: string. optional. use style.textVerticalAlign by default.
+   *         }
+   */
+  calculateTextPosition: null
+};
+zrUtil.inherits(Displayable, Element);
+zrUtil.mixin(Displayable, RectText); // zrUtil.mixin(Displayable, Stateful);
+
+var _default = Displayable;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Gradient.js":
+/*!******************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Gradient.js ***!
+  \******************************************************/
+/*! no static exports found */
+/***/ (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
+    });
+  }
+};
+var _default = Gradient;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Image.js":
+/*!***************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Image.js ***!
+  \***************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Displayable = __webpack_require__(/*! ./Displayable */ "./node_modules/zrender/lib/graphic/Displayable.js");
+
+var BoundingRect = __webpack_require__(/*! ../core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var imageHelper = __webpack_require__(/*! ./helper/image */ "./node_modules/zrender/lib/graphic/helper/image.js");
+
+/**
+ * @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; // Must bind each time
+
+    style.bind(ctx, this, prevEl);
+    var image = this._image = imageHelper.createOrUpdateImage(src, this._image, this, this.onload);
+
+    if (!image || !imageHelper.isImageReady(image)) {
+      return;
+    } // 图片已经加载完成
+    // if (image.nodeName.toUpperCase() == 'IMG') {
+    //     if (!image.complete) {
+    //         return;
+    //     }
+    // }
+    // Else is canvas
+
+
+    var x = style.x || 0;
+    var y = style.y || 0;
+    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);
+    } // Draw rect text
+
+
+    if (style.text != null) {
+      // Only restore transform when needs draw text.
+      this.restoreTransform(ctx);
+      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);
+var _default = ZImage;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/IncrementalDisplayable.js":
+/*!********************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/IncrementalDisplayable.js ***!
+  \********************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _util = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var inherits = _util.inherits;
+
+var Displayble = __webpack_require__(/*! ./Displayable */ "./node_modules/zrender/lib/graphic/Displayable.js");
+
+var BoundingRect = __webpack_require__(/*! ../core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+/**
+ * Displayable for incremental rendering. It will be rendered in a separate layer
+ * IncrementalDisplay have two main methods. `clearDisplayables` and `addDisplayables`
+ * addDisplayables will render the added displayables incremetally.
+ *
+ * It use a not clearFlag to tell the painter don't clear the layer if it's the first element.
+ */
+// TODO Style override ?
+function IncrementalDisplayble(opts) {
+  Displayble.call(this, opts);
+  this._displayables = [];
+  this._temporaryDisplayables = [];
+  this._cursor = 0;
+  this.notClear = true;
+}
+
+IncrementalDisplayble.prototype.incremental = true;
+
+IncrementalDisplayble.prototype.clearDisplaybles = function () {
+  this._displayables = [];
+  this._temporaryDisplayables = [];
+  this._cursor = 0;
+  this.dirty();
+  this.notClear = false;
+};
+
+IncrementalDisplayble.prototype.addDisplayable = function (displayable, notPersistent) {
+  if (notPersistent) {
+    this._temporaryDisplayables.push(displayable);
+  } else {
+    this._displayables.push(displayable);
+  }
+
+  this.dirty();
+};
+
+IncrementalDisplayble.prototype.addDisplayables = function (displayables, notPersistent) {
+  notPersistent = notPersistent || false;
+
+  for (var i = 0; i < displayables.length; i++) {
+    this.addDisplayable(displayables[i], notPersistent);
+  }
+};
+
+IncrementalDisplayble.prototype.eachPendingDisplayable = function (cb) {
+  for (var i = this._cursor; i < this._displayables.length; i++) {
+    cb && cb(this._displayables[i]);
+  }
+
+  for (var i = 0; i < this._temporaryDisplayables.length; i++) {
+    cb && cb(this._temporaryDisplayables[i]);
+  }
+};
+
+IncrementalDisplayble.prototype.update = function () {
+  this.updateTransform();
+
+  for (var i = this._cursor; i < this._displayables.length; i++) {
+    var displayable = this._displayables[i]; // PENDING
+
+    displayable.parent = this;
+    displayable.update();
+    displayable.parent = null;
+  }
+
+  for (var i = 0; i < this._temporaryDisplayables.length; i++) {
+    var displayable = this._temporaryDisplayables[i]; // PENDING
+
+    displayable.parent = this;
+    displayable.update();
+    displayable.parent = null;
+  }
+};
+
+IncrementalDisplayble.prototype.brush = function (ctx, prevEl) {
+  // Render persistant displayables.
+  for (var i = this._cursor; i < this._displayables.length; i++) {
+    var displayable = this._displayables[i];
+    displayable.beforeBrush && displayable.beforeBrush(ctx);
+    displayable.brush(ctx, i === this._cursor ? null : this._displayables[i - 1]);
+    displayable.afterBrush && displayable.afterBrush(ctx);
+  }
+
+  this._cursor = i; // Render temporary displayables.
+
+  for (var i = 0; i < this._temporaryDisplayables.length; i++) {
+    var displayable = this._temporaryDisplayables[i];
+    displayable.beforeBrush && displayable.beforeBrush(ctx);
+    displayable.brush(ctx, i === 0 ? null : this._temporaryDisplayables[i - 1]);
+    displayable.afterBrush && displayable.afterBrush(ctx);
+  }
+
+  this._temporaryDisplayables = [];
+  this.notClear = true;
+};
+
+var m = [];
+
+IncrementalDisplayble.prototype.getBoundingRect = function () {
+  if (!this._rect) {
+    var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity);
+
+    for (var i = 0; i < this._displayables.length; i++) {
+      var displayable = this._displayables[i];
+      var childRect = displayable.getBoundingRect().clone();
+
+      if (displayable.needLocalTransform()) {
+        childRect.applyTransform(displayable.getLocalTransform(m));
+      }
+
+      rect.union(childRect);
+    }
+
+    this._rect = rect;
+  }
+
+  return this._rect;
+};
+
+IncrementalDisplayble.prototype.contain = function (x, y) {
+  var localPos = this.transformCoordToLocal(x, y);
+  var rect = this.getBoundingRect();
+
+  if (rect.contain(localPos[0], localPos[1])) {
+    for (var i = 0; i < this._displayables.length; i++) {
+      var displayable = this._displayables[i];
+
+      if (displayable.contain(x, y)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+};
+
+inherits(IncrementalDisplayble, Displayble);
+var _default = IncrementalDisplayble;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/LinearGradient.js":
+/*!************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/LinearGradient.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var Gradient = __webpack_require__(/*! ./Gradient */ "./node_modules/zrender/lib/graphic/Gradient.js");
+
+/**
+ * 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);
+var _default = LinearGradient;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Path.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Path.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Displayable = __webpack_require__(/*! ./Displayable */ "./node_modules/zrender/lib/graphic/Displayable.js");
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var PathProxy = __webpack_require__(/*! ../core/PathProxy */ "./node_modules/zrender/lib/core/PathProxy.js");
+
+var pathContain = __webpack_require__(/*! ../contain/path */ "./node_modules/zrender/lib/contain/path.js");
+
+var Pattern = __webpack_require__(/*! ./Pattern */ "./node_modules/zrender/lib/graphic/Pattern.js");
+
+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,
+  // This item default to be false. But in map series in echarts,
+  // in order to improve performance, it should be set to true,
+  // so the shorty segment won't draw.
+  segmentIgnoreThreshold: 0,
+
+  /**
+   * See `module:zrender/src/graphic/helper/subPixelOptimize`.
+   * @type {boolean}
+   */
+  subPixelOptimize: false,
+  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], this.segmentIgnoreThreshold); // 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);
+    }
+
+    if (hasFill) {
+      if (style.fillOpacity != null) {
+        var originalGlobalAlpha = ctx.globalAlpha;
+        ctx.globalAlpha = style.fillOpacity * style.opacity;
+        path.fill(ctx);
+        ctx.globalAlpha = originalGlobalAlpha;
+      } else {
+        path.fill(ctx);
+      }
+    }
+
+    if (lineDash && ctxLineDash) {
+      ctx.setLineDash(lineDash);
+      ctx.lineDashOffset = lineDashOffset;
+    }
+
+    if (hasStroke) {
+      if (style.strokeOpacity != null) {
+        var originalGlobalAlpha = ctx.globalAlpha;
+        ctx.globalAlpha = style.strokeOpacity * style.opacity;
+        path.stroke(ctx);
+        ctx.globalAlpha = originalGlobalAlpha;
+      } else {
+        path.stroke(ctx);
+      }
+    }
+
+    if (lineDash && ctxLineDash) {
+      // PENDING
+      // Remove lineDash
+      ctx.setLineDash([]);
+    } // Draw rect text
+
+
+    if (style.text != null) {
+      // Only restore transform when needs draw text.
+      this.restoreTransform(ctx);
+      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 = this.__dirtyText = 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);
+var _default = Path;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Pattern.js":
+/*!*****************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Pattern.js ***!
+  \*****************************************************/
+/*! no static exports found */
+/***/ (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');
+};
+
+var _default = Pattern;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/RadialGradient.js":
+/*!************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/RadialGradient.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var Gradient = __webpack_require__(/*! ./Gradient */ "./node_modules/zrender/lib/graphic/Gradient.js");
+
+/**
+ * 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);
+var _default = RadialGradient;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Style.js":
+/*!***************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Style.js ***!
+  \***************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var fixShadow = __webpack_require__(/*! ./helper/fixShadow */ "./node_modules/zrender/lib/graphic/helper/fixShadow.js");
+
+var _constant = __webpack_require__(/*! ./constant */ "./node_modules/zrender/lib/graphic/constant.js");
+
+var ContextCachedBy = _constant.ContextCachedBy;
+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, false);
+};
+
+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;
+  } // Fix NaN when rect is Infinity
+
+
+  x = isNaN(x) ? 0 : x;
+  x2 = isNaN(x2) ? 1 : x2;
+  y = isNaN(y) ? 0 : y;
+  y2 = isNaN(y2) ? 0 : y2;
+  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: '#000',
+
+  /**
+   * @type {string}
+   */
+  stroke: null,
+
+  /**
+   * @type {number}
+   */
+  opacity: 1,
+
+  /**
+   * @type {number}
+   */
+  fillOpacity: null,
+
+  /**
+   * @type {number}
+   */
+  strokeOpacity: null,
+
+  /**
+   * `true` is not supported.
+   * `false`/`null`/`undefined` are the same.
+   * `false` is used to remove lineDash in some
+   * case that `null`/`undefined` can not be set.
+   * (e.g., emphasis.lineStyle in echarts)
+   * @type {Array.<number>|boolean}
+   */
+  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,
+
+  /**
+   * If `fontSize` or `fontFamily` exists, `font` will be reset by
+   * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
+   * So do not visit it directly in upper application (like echarts),
+   * but use `contain/text#makeFont` instead.
+   * @type {string}
+   */
+  font: null,
+
+  /**
+   * The same as font. Use font please.
+   * @deprecated
+   * @type {string}
+   */
+  textFont: null,
+
+  /**
+   * It helps merging respectively, rather than parsing an entire font string.
+   * @type {string}
+   */
+  fontStyle: null,
+
+  /**
+   * It helps merging respectively, rather than parsing an entire font string.
+   * @type {string}
+   */
+  fontWeight: null,
+
+  /**
+   * It helps merging respectively, rather than parsing an entire font string.
+   * Should be 12 but not '12px'.
+   * @type {number}
+   */
+  fontSize: null,
+
+  /**
+   * It helps merging respectively, rather than parsing an entire font string.
+   * @type {string}
+   */
+  fontFamily: null,
+
+  /**
+   * Reserved for special functinality, like 'hr'.
+   * @type {string}
+   */
+  textTag: null,
+
+  /**
+   * @type {string}
+   */
+  textFill: '#000',
+
+  /**
+   * @type {string}
+   */
+  textStroke: null,
+
+  /**
+   * @type {number}
+   */
+  textWidth: null,
+
+  /**
+   * Only for textBackground.
+   * @type {number}
+   */
+  textHeight: null,
+
+  /**
+   * textStroke may be set as some color as a default
+   * value in upper applicaion, where the default value
+   * of textStrokeWidth should be 0 to make sure that
+   * user can choose to do not use text stroke.
+   * @type {number}
+   */
+  textStrokeWidth: 0,
+
+  /**
+   * @type {number}
+   */
+  textLineHeight: null,
+
+  /**
+   * 'inside', 'left', 'right', 'top', 'bottom'
+   * [x, y]
+   * Based on x, y of rect.
+   * @type {string|Array.<number>}
+   * @default 'inside'
+   */
+  textPosition: 'inside',
+
+  /**
+   * If not specified, use the boundingRect of a `displayable`.
+   * @type {Object}
+   */
+  textRect: null,
+
+  /**
+   * [x, y]
+   * @type {Array.<number>}
+   */
+  textOffset: null,
+
+  /**
+   * @type {string}
+   */
+  textAlign: null,
+
+  /**
+   * @type {string}
+   */
+  textVerticalAlign: null,
+
+  /**
+   * @type {number}
+   */
+  textDistance: 5,
+
+  /**
+   * @type {string}
+   */
+  textShadowColor: 'transparent',
+
+  /**
+   * @type {number}
+   */
+  textShadowBlur: 0,
+
+  /**
+   * @type {number}
+   */
+  textShadowOffsetX: 0,
+
+  /**
+   * @type {number}
+   */
+  textShadowOffsetY: 0,
+
+  /**
+   * @type {string}
+   */
+  textBoxShadowColor: 'transparent',
+
+  /**
+   * @type {number}
+   */
+  textBoxShadowBlur: 0,
+
+  /**
+   * @type {number}
+   */
+  textBoxShadowOffsetX: 0,
+
+  /**
+   * @type {number}
+   */
+  textBoxShadowOffsetY: 0,
+
+  /**
+   * Whether transform text.
+   * Only available in Path and Image element,
+   * where the text is called as `RectText`.
+   * @type {boolean}
+   */
+  transformText: false,
+
+  /**
+   * Text rotate around position of Path or Image.
+   * The origin of the rotation can be specified by `textOrigin`.
+   * Only available in Path and Image element,
+   * where the text is called as `RectText`.
+   */
+  textRotation: 0,
+
+  /**
+   * Text origin of text rotation.
+   * Useful in the case like label rotation of circular symbol.
+   * Only available in Path and Image element, where the text is called
+   * as `RectText` and the element is called as "host element".
+   * The value can be:
+   * + If specified as a coordinate like `[10, 40]`, it is the `[x, y]`
+   * base on the left-top corner of the rect of its host element.
+   * + If specified as a string `center`, it is the center of the rect of
+   * its host element.
+   * + By default, this origin is the `textPosition`.
+   * @type {string|Array.<number>}
+   */
+  textOrigin: null,
+
+  /**
+   * @type {string}
+   */
+  textBackgroundColor: null,
+
+  /**
+   * @type {string}
+   */
+  textBorderColor: null,
+
+  /**
+   * @type {number}
+   */
+  textBorderWidth: 0,
+
+  /**
+   * @type {number}
+   */
+  textBorderRadius: 0,
+
+  /**
+   * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
+   * @type {number|Array.<number>}
+   */
+  textPadding: null,
+
+  /**
+   * Text styles for rich text.
+   * @type {Object}
+   */
+  rich: null,
+
+  /**
+   * {outerWidth, outerHeight, ellipsis, placeholder}
+   * @type {Object}
+   */
+  truncate: null,
+
+  /**
+   * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
+   * @type {string}
+   */
+  blend: null,
+
+  /**
+   * @param {CanvasRenderingContext2D} ctx
+   */
+  bind: function (ctx, el, prevEl) {
+    var style = this;
+    var prevStyle = prevEl && prevEl.style; // If no prevStyle, it means first draw.
+    // Only apply cache if the last time cachced by this function.
+
+    var notCheckCache = !prevStyle || ctx.__attrCachedBy !== ContextCachedBy.STYLE_BIND;
+    ctx.__attrCachedBy = ContextCachedBy.STYLE_BIND;
+
+    for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
+      var prop = STYLE_COMMON_PROPS[i];
+      var styleName = prop[0];
+
+      if (notCheckCache || style[styleName] !== prevStyle[styleName]) {
+        // FIXME Invalid property value will cause style leak from previous element.
+        ctx[styleName] = fixShadow(ctx, styleName, style[styleName] || prop[1]);
+      }
+    }
+
+    if (notCheckCache || style.fill !== prevStyle.fill) {
+      ctx.fillStyle = style.fill;
+    }
+
+    if (notCheckCache || style.stroke !== prevStyle.stroke) {
+      ctx.strokeStyle = style.stroke;
+    }
+
+    if (notCheckCache || style.opacity !== prevStyle.opacity) {
+      ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
+    }
+
+    if (notCheckCache || 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 true: overwrirte any way.
+   *                            false: overwrite only when !target.hasOwnProperty
+   *                            others: overwrite when property is not null/undefined.
+   */
+  extendFrom: function (otherStyle, overwrite) {
+    if (otherStyle) {
+      for (var name in otherStyle) {
+        if (otherStyle.hasOwnProperty(name) && (overwrite === true || (overwrite === false ? !this.hasOwnProperty(name) : otherStyle[name] != null))) {
+          this[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;
+var _default = Style;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/Text.js":
+/*!**************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/Text.js ***!
+  \**************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Displayable = __webpack_require__(/*! ./Displayable */ "./node_modules/zrender/lib/graphic/Displayable.js");
+
+var zrUtil = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var textContain = __webpack_require__(/*! ../contain/text */ "./node_modules/zrender/lib/contain/text.js");
+
+var textHelper = __webpack_require__(/*! ./helper/text */ "./node_modules/zrender/lib/graphic/helper/text.js");
+
+var _constant = __webpack_require__(/*! ./constant */ "./node_modules/zrender/lib/graphic/constant.js");
+
+var ContextCachedBy = _constant.ContextCachedBy;
+
+/**
+ * @alias zrender/graphic/Text
+ * @extends module:zrender/graphic/Displayable
+ * @constructor
+ * @param {Object} opts
+ */
+var Text = function (opts) {
+  // jshint ignore:line
+  Displayable.call(this, opts);
+};
+
+Text.prototype = {
+  constructor: Text,
+  type: 'text',
+  brush: function (ctx, prevEl) {
+    var style = this.style; // Optimize, avoid normalize every time.
+
+    this.__dirty && textHelper.normalizeTextStyle(style, true); // Use props with prefix 'text'.
+
+    style.fill = style.stroke = style.shadowBlur = style.shadowColor = style.shadowOffsetX = style.shadowOffsetY = null;
+    var text = style.text; // Convert to string
+
+    text != null && (text += ''); // Do not apply style.bind in Text node. Because the real bind job
+    // is in textHelper.renderText, and performance of text render should
+    // be considered.
+    // style.bind(ctx, this, prevEl);
+
+    if (!textHelper.needDrawText(text, style)) {
+      // The current el.style is not applied
+      // and should not be used as cache.
+      ctx.__attrCachedBy = ContextCachedBy.NONE;
+      return;
+    }
+
+    this.setTransform(ctx);
+    textHelper.renderText(this, ctx, text, style, null, prevEl);
+    this.restoreTransform(ctx);
+  },
+  getBoundingRect: function () {
+    var style = this.style; // Optimize, avoid normalize every time.
+
+    this.__dirty && textHelper.normalizeTextStyle(style, true);
+
+    if (!this._rect) {
+      var text = style.text;
+      text != null ? text += '' : text = '';
+      var rect = textContain.getBoundingRect(style.text + '', style.font, style.textAlign, style.textVerticalAlign, style.textPadding, style.textLineHeight, style.rich);
+      rect.x += style.x || 0;
+      rect.y += style.y || 0;
+
+      if (textHelper.getStroke(style.textStroke, style.textStrokeWidth)) {
+        var w = style.textStrokeWidth;
+        rect.x -= w / 2;
+        rect.y -= w / 2;
+        rect.width += w;
+        rect.height += w;
+      }
+
+      this._rect = rect;
+    }
+
+    return this._rect;
+  }
+};
+zrUtil.inherits(Text, Displayable);
+var _default = Text;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/constant.js":
+/*!******************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/constant.js ***!
+  \******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+var ContextCachedBy = {
+  NONE: 0,
+  STYLE_BIND: 1,
+  PLAIN_TEXT: 2
+}; // Avoid confused with 0/false.
+
+var WILL_BE_RESTORED = 9;
+exports.ContextCachedBy = ContextCachedBy;
+exports.WILL_BE_RESTORED = WILL_BE_RESTORED;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/fixClipWithShadow.js":
+/*!**********************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/fixClipWithShadow.js ***!
+  \**********************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var env = __webpack_require__(/*! ../../core/env */ "./node_modules/zrender/lib/core/env.js");
+
+// Fix weird bug in some version of IE11 (like 11.0.9600.178**),
+// where exception "unexpected call to method or property access"
+// might be thrown when calling ctx.fill or ctx.stroke after a path
+// whose area size is zero is drawn and ctx.clip() is called and
+// shadowBlur is set. See #4572, #3112, #5777.
+// (e.g.,
+//  ctx.moveTo(10, 10);
+//  ctx.lineTo(20, 10);
+//  ctx.closePath();
+//  ctx.clip();
+//  ctx.shadowBlur = 10;
+//  ...
+//  ctx.fill();
+// )
+var shadowTemp = [['shadowBlur', 0], ['shadowColor', '#000'], ['shadowOffsetX', 0], ['shadowOffsetY', 0]];
+
+function _default(orignalBrush) {
+  // version string can be: '11.0'
+  return env.browser.ie && env.browser.version >= 11 ? function () {
+    var clipPaths = this.__clipPaths;
+    var style = this.style;
+    var modified;
+
+    if (clipPaths) {
+      for (var i = 0; i < clipPaths.length; i++) {
+        var clipPath = clipPaths[i];
+        var shape = clipPath && clipPath.shape;
+        var type = clipPath && clipPath.type;
+
+        if (shape && (type === 'sector' && shape.startAngle === shape.endAngle || type === 'rect' && (!shape.width || !shape.height))) {
+          for (var j = 0; j < shadowTemp.length; j++) {
+            // It is save to put shadowTemp static, because shadowTemp
+            // will be all modified each item brush called.
+            shadowTemp[j][2] = style[shadowTemp[j][0]];
+            style[shadowTemp[j][0]] = shadowTemp[j][1];
+          }
+
+          modified = true;
+          break;
+        }
+      }
+    }
+
+    orignalBrush.apply(this, arguments);
+
+    if (modified) {
+      for (var j = 0; j < shadowTemp.length; j++) {
+        style[shadowTemp[j][0]] = shadowTemp[j][2];
+      }
+    }
+  } : orignalBrush;
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/fixShadow.js":
+/*!**************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/fixShadow.js ***!
+  \**************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+var SHADOW_PROPS = {
+  'shadowBlur': 1,
+  'shadowOffsetX': 1,
+  'shadowOffsetY': 1,
+  'textShadowBlur': 1,
+  'textShadowOffsetX': 1,
+  'textShadowOffsetY': 1,
+  'textBoxShadowBlur': 1,
+  'textBoxShadowOffsetX': 1,
+  'textBoxShadowOffsetY': 1
+};
+
+function _default(ctx, propName, value) {
+  if (SHADOW_PROPS.hasOwnProperty(propName)) {
+    return value *= ctx.dpr;
+  }
+
+  return value;
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/image.js":
+/*!**********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/image.js ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var LRU = __webpack_require__(/*! ../../core/LRU */ "./node_modules/zrender/lib/core/LRU.js");
+
+var globalImageCache = new LRU(50);
+/**
+ * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
+ * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
+ */
+
+function findExistImage(newImageOrSrc) {
+  if (typeof newImageOrSrc === 'string') {
+    var cachedImgObj = globalImageCache.get(newImageOrSrc);
+    return cachedImgObj && cachedImgObj.image;
+  } else {
+    return newImageOrSrc;
+  }
+}
+/**
+ * Caution: User should cache loaded images, but not just count on LRU.
+ * Consider if required images more than LRU size, will dead loop occur?
+ *
+ * @param {string|HTMLImageElement|HTMLCanvasElement|Canvas} newImageOrSrc
+ * @param {HTMLImageElement|HTMLCanvasElement|Canvas} image Existent image.
+ * @param {module:zrender/Element} [hostEl] For calling `dirty`.
+ * @param {Function} [cb] params: (image, cbPayload)
+ * @param {Object} [cbPayload] Payload on cb calling.
+ * @return {HTMLImageElement|HTMLCanvasElement|Canvas} image
+ */
+
+
+function createOrUpdateImage(newImageOrSrc, image, hostEl, cb, cbPayload) {
+  if (!newImageOrSrc) {
+    return image;
+  } else if (typeof newImageOrSrc === 'string') {
+    // Image should not be loaded repeatly.
+    if (image && image.__zrImageSrc === newImageOrSrc || !hostEl) {
+      return image;
+    } // Only when there is no existent image or existent image src
+    // is different, this method is responsible for load.
+
+
+    var cachedImgObj = globalImageCache.get(newImageOrSrc);
+    var pendingWrap = {
+      hostEl: hostEl,
+      cb: cb,
+      cbPayload: cbPayload
+    };
+
+    if (cachedImgObj) {
+      image = cachedImgObj.image;
+      !isImageReady(image) && cachedImgObj.pending.push(pendingWrap);
+    } else {
+      image = new Image();
+      image.onload = image.onerror = imageOnLoad;
+      globalImageCache.put(newImageOrSrc, image.__cachedImgObj = {
+        image: image,
+        pending: [pendingWrap]
+      });
+      image.src = image.__zrImageSrc = newImageOrSrc;
+    }
+
+    return image;
+  } // newImageOrSrc is an HTMLImageElement or HTMLCanvasElement or Canvas
+  else {
+      return newImageOrSrc;
+    }
+}
+
+function imageOnLoad() {
+  var cachedImgObj = this.__cachedImgObj;
+  this.onload = this.onerror = this.__cachedImgObj = null;
+
+  for (var i = 0; i < cachedImgObj.pending.length; i++) {
+    var pendingWrap = cachedImgObj.pending[i];
+    var cb = pendingWrap.cb;
+    cb && cb(this, pendingWrap.cbPayload);
+    pendingWrap.hostEl.dirty();
+  }
+
+  cachedImgObj.pending.length = 0;
+}
+
+function isImageReady(image) {
+  return image && image.width && image.height;
+}
+
+exports.findExistImage = findExistImage;
+exports.createOrUpdateImage = createOrUpdateImage;
+exports.isImageReady = isImageReady;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/poly.js":
+/*!*********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/poly.js ***!
+  \*********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var smoothSpline = __webpack_require__(/*! ./smoothSpline */ "./node_modules/zrender/lib/graphic/helper/smoothSpline.js");
+
+var smoothBezier = __webpack_require__(/*! ./smoothBezier */ "./node_modules/zrender/lib/graphic/helper/smoothBezier.js");
+
+function buildPath(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();
+  }
+}
+
+exports.buildPath = buildPath;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/roundRect.js":
+/*!**************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/roundRect.js ***!
+  \**************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * @param {Object} ctx
+ * @param {Object} shape
+ * @param {number} shape.x
+ * @param {number} shape.y
+ * @param {number} shape.width
+ * @param {number} shape.height
+ * @param {number} shape.r
+ */
+function buildPath(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.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0);
+  ctx.lineTo(x + width, y + height - r3);
+  r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2);
+  ctx.lineTo(x + r4, y + height);
+  r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI);
+  ctx.lineTo(x, y + r1);
+  r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5);
+}
+
+exports.buildPath = buildPath;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/smoothBezier.js":
+/*!*****************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/smoothBezier.js ***!
+  \*****************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _vector = __webpack_require__(/*! ../../core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var v2Min = _vector.min;
+var v2Max = _vector.max;
+var v2Scale = _vector.scale;
+var v2Distance = _vector.distance;
+var v2Add = _vector.add;
+var v2Clone = _vector.clone;
+var v2Sub = _vector.sub;
+
+/**
+ * 贝塞尔平滑曲线
+ * @module zrender/shape/util/smoothBezier
+ * @author pissang (https://www.github.com/pissang)
+ *         Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *         errorrik (errorrik@gmail.com)
+ */
+
+/**
+ * 贝塞尔平滑曲线
+ * @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} 计算出来的控制点数组
+ */
+function _default(points, smooth, isLoop, constraint) {
+  var cps = [];
+  var v = [];
+  var v1 = [];
+  var v2 = [];
+  var prevPoint;
+  var nextPoint;
+  var min;
+  var 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(v2Clone(points[i]));
+        continue;
+      } else {
+        prevPoint = points[i - 1];
+        nextPoint = points[i + 1];
+      }
+    }
+
+    v2Sub(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;
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/smoothSpline.js":
+/*!*****************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/smoothSpline.js ***!
+  \*****************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _vector = __webpack_require__(/*! ../../core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var v2Distance = _vector.distance;
+
+/**
+ * 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)
+ */
+
+/**
+ * @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}
+ */
+
+
+function _default(points, isLoop) {
+  var len = points.length;
+  var ret = [];
+  var distance = 0;
+
+  for (var i = 1; i < len; i++) {
+    distance += v2Distance(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;
+}
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/subPixelOptimize.js":
+/*!*********************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/subPixelOptimize.js ***!
+  \*********************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * Sub-pixel optimize for canvas rendering, prevent from blur
+ * when rendering a thin vertical/horizontal line.
+ */
+var round = Math.round;
+/**
+ * Sub pixel optimize line for canvas
+ *
+ * @param {Object} outputShape The modification will be performed on `outputShape`.
+ *                 `outputShape` and `inputShape` can be the same object.
+ *                 `outputShape` object can be used repeatly, because all of
+ *                 the `x1`, `x2`, `y1`, `y2` will be assigned in this method.
+ * @param {Object} [inputShape]
+ * @param {number} [inputShape.x1]
+ * @param {number} [inputShape.y1]
+ * @param {number} [inputShape.x2]
+ * @param {number} [inputShape.y2]
+ * @param {Object} [style]
+ * @param {number} [style.lineWidth] If `null`/`undefined`/`0`, do not optimize.
+ */
+
+function subPixelOptimizeLine(outputShape, inputShape, style) {
+  if (!inputShape) {
+    return;
+  }
+
+  var x1 = inputShape.x1;
+  var x2 = inputShape.x2;
+  var y1 = inputShape.y1;
+  var y2 = inputShape.y2;
+  outputShape.x1 = x1;
+  outputShape.x2 = x2;
+  outputShape.y1 = y1;
+  outputShape.y2 = y2;
+  var lineWidth = style && style.lineWidth;
+
+  if (!lineWidth) {
+    return;
+  }
+
+  if (round(x1 * 2) === round(x2 * 2)) {
+    outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true);
+  }
+
+  if (round(y1 * 2) === round(y2 * 2)) {
+    outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true);
+  }
+}
+/**
+ * Sub pixel optimize rect for canvas
+ *
+ * @param {Object} outputShape The modification will be performed on `outputShape`.
+ *                 `outputShape` and `inputShape` can be the same object.
+ *                 `outputShape` object can be used repeatly, because all of
+ *                 the `x`, `y`, `width`, `height` will be assigned in this method.
+ * @param {Object} [inputShape]
+ * @param {number} [inputShape.x]
+ * @param {number} [inputShape.y]
+ * @param {number} [inputShape.width]
+ * @param {number} [inputShape.height]
+ * @param {Object} [style]
+ * @param {number} [style.lineWidth] If `null`/`undefined`/`0`, do not optimize.
+ */
+
+
+function subPixelOptimizeRect(outputShape, inputShape, style) {
+  if (!inputShape) {
+    return;
+  }
+
+  var originX = inputShape.x;
+  var originY = inputShape.y;
+  var originWidth = inputShape.width;
+  var originHeight = inputShape.height;
+  outputShape.x = originX;
+  outputShape.y = originY;
+  outputShape.width = originWidth;
+  outputShape.height = originHeight;
+  var lineWidth = style && style.lineWidth;
+
+  if (!lineWidth) {
+    return;
+  }
+
+  outputShape.x = subPixelOptimize(originX, lineWidth, true);
+  outputShape.y = subPixelOptimize(originY, lineWidth, true);
+  outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1);
+  outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1);
+}
+/**
+ * Sub pixel optimize for canvas
+ *
+ * @param {number} position Coordinate, such as x, y
+ * @param {number} lineWidth If `null`/`undefined`/`0`, do not optimize.
+ * @param {boolean=} positiveOrNegative Default false (negative).
+ * @return {number} Optimized position.
+ */
+
+
+function subPixelOptimize(position, lineWidth, positiveOrNegative) {
+  if (!lineWidth) {
+    return position;
+  } // 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;
+}
+
+exports.subPixelOptimizeLine = subPixelOptimizeLine;
+exports.subPixelOptimizeRect = subPixelOptimizeRect;
+exports.subPixelOptimize = subPixelOptimize;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/helper/text.js":
+/*!*********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/helper/text.js ***!
+  \*********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var _util = __webpack_require__(/*! ../../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var retrieve2 = _util.retrieve2;
+var retrieve3 = _util.retrieve3;
+var each = _util.each;
+var normalizeCssArray = _util.normalizeCssArray;
+var isString = _util.isString;
+var isObject = _util.isObject;
+
+var textContain = __webpack_require__(/*! ../../contain/text */ "./node_modules/zrender/lib/contain/text.js");
+
+var roundRectHelper = __webpack_require__(/*! ./roundRect */ "./node_modules/zrender/lib/graphic/helper/roundRect.js");
+
+var imageHelper = __webpack_require__(/*! ./image */ "./node_modules/zrender/lib/graphic/helper/image.js");
+
+var fixShadow = __webpack_require__(/*! ./fixShadow */ "./node_modules/zrender/lib/graphic/helper/fixShadow.js");
+
+var _constant = __webpack_require__(/*! ../constant */ "./node_modules/zrender/lib/graphic/constant.js");
+
+var ContextCachedBy = _constant.ContextCachedBy;
+var WILL_BE_RESTORED = _constant.WILL_BE_RESTORED;
+var DEFAULT_FONT = textContain.DEFAULT_FONT; // TODO: Have not support 'start', 'end' yet.
+
+var VALID_TEXT_ALIGN = {
+  left: 1,
+  right: 1,
+  center: 1
+};
+var VALID_TEXT_VERTICAL_ALIGN = {
+  top: 1,
+  bottom: 1,
+  middle: 1
+}; // Different from `STYLE_COMMON_PROPS` of `graphic/Style`,
+// the default value of shadowColor is `'transparent'`.
+
+var SHADOW_STYLE_COMMON_PROPS = [['textShadowBlur', 'shadowBlur', 0], ['textShadowOffsetX', 'shadowOffsetX', 0], ['textShadowOffsetY', 'shadowOffsetY', 0], ['textShadowColor', 'shadowColor', 'transparent']];
+var _tmpTextPositionResult = {};
+var _tmpBoxPositionResult = {};
+/**
+ * @param {module:zrender/graphic/Style} style
+ * @return {module:zrender/graphic/Style} The input style.
+ */
+
+function normalizeTextStyle(style) {
+  normalizeStyle(style);
+  each(style.rich, normalizeStyle);
+  return style;
+}
+
+function normalizeStyle(style) {
+  if (style) {
+    style.font = textContain.makeFont(style);
+    var textAlign = style.textAlign;
+    textAlign === 'middle' && (textAlign = 'center');
+    style.textAlign = textAlign == null || VALID_TEXT_ALIGN[textAlign] ? textAlign : 'left'; // Compatible with textBaseline.
+
+    var textVerticalAlign = style.textVerticalAlign || style.textBaseline;
+    textVerticalAlign === 'center' && (textVerticalAlign = 'middle');
+    style.textVerticalAlign = textVerticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign] ? textVerticalAlign : 'top';
+    var textPadding = style.textPadding;
+
+    if (textPadding) {
+      style.textPadding = normalizeCssArray(style.textPadding);
+    }
+  }
+}
+/**
+ * @param {CanvasRenderingContext2D} ctx
+ * @param {string} text
+ * @param {module:zrender/graphic/Style} style
+ * @param {Object|boolean} [rect] {x, y, width, height}
+ *                  If set false, rect text is not used.
+ * @param {Element|module:zrender/graphic/helper/constant.WILL_BE_RESTORED} [prevEl] For ctx prop cache.
+ */
+
+
+function renderText(hostEl, ctx, text, style, rect, prevEl) {
+  style.rich ? renderRichText(hostEl, ctx, text, style, rect, prevEl) : renderPlainText(hostEl, ctx, text, style, rect, prevEl);
+} // Avoid setting to ctx according to prevEl if possible for
+// performance in scenarios of large amount text.
+
+
+function renderPlainText(hostEl, ctx, text, style, rect, prevEl) {
+  'use strict';
+
+  var needDrawBg = needDrawBackground(style);
+  var prevStyle;
+  var checkCache = false;
+  var cachedByMe = ctx.__attrCachedBy === ContextCachedBy.PLAIN_TEXT; // Only take and check cache for `Text` el, but not RectText.
+
+  if (prevEl !== WILL_BE_RESTORED) {
+    if (prevEl) {
+      prevStyle = prevEl.style;
+      checkCache = !needDrawBg && cachedByMe && prevStyle;
+    } // Prevent from using cache in `Style::bind`, because of the case:
+    // ctx property is modified by other properties than `Style::bind`
+    // used, and Style::bind is called next.
+
+
+    ctx.__attrCachedBy = needDrawBg ? ContextCachedBy.NONE : ContextCachedBy.PLAIN_TEXT;
+  } // Since this will be restored, prevent from using these props to check cache in the next
+  // entering of this method. But do not need to clear other cache like `Style::bind`.
+  else if (cachedByMe) {
+      ctx.__attrCachedBy = ContextCachedBy.NONE;
+    }
+
+  var styleFont = style.font || DEFAULT_FONT; // PENDING
+  // Only `Text` el set `font` and keep it (`RectText` will restore). So theoretically
+  // we can make font cache on ctx, which can cache for text el that are discontinuous.
+  // But layer save/restore needed to be considered.
+  // if (styleFont !== ctx.__fontCache) {
+  //     ctx.font = styleFont;
+  //     if (prevEl !== WILL_BE_RESTORED) {
+  //         ctx.__fontCache = styleFont;
+  //     }
+  // }
+
+  if (!checkCache || styleFont !== (prevStyle.font || DEFAULT_FONT)) {
+    ctx.font = styleFont;
+  } // Use the final font from context-2d, because the final
+  // font might not be the style.font when it is illegal.
+  // But get `ctx.font` might be time consuming.
+
+
+  var computedFont = hostEl.__computedFont;
+
+  if (hostEl.__styleFont !== styleFont) {
+    hostEl.__styleFont = styleFont;
+    computedFont = hostEl.__computedFont = ctx.font;
+  }
+
+  var textPadding = style.textPadding;
+  var textLineHeight = style.textLineHeight;
+  var contentBlock = hostEl.__textCotentBlock;
+
+  if (!contentBlock || hostEl.__dirtyText) {
+    contentBlock = hostEl.__textCotentBlock = textContain.parsePlainText(text, computedFont, textPadding, textLineHeight, style.truncate);
+  }
+
+  var outerHeight = contentBlock.outerHeight;
+  var textLines = contentBlock.lines;
+  var lineHeight = contentBlock.lineHeight;
+  var boxPos = getBoxPosition(_tmpBoxPositionResult, hostEl, style, rect);
+  var baseX = boxPos.baseX;
+  var baseY = boxPos.baseY;
+  var textAlign = boxPos.textAlign || 'left';
+  var textVerticalAlign = boxPos.textVerticalAlign; // Origin of textRotation should be the base point of text drawing.
+
+  applyTextRotation(ctx, style, rect, baseX, baseY);
+  var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);
+  var textX = baseX;
+  var textY = boxY;
+
+  if (needDrawBg || textPadding) {
+    // Consider performance, do not call getTextWidth util necessary.
+    var textWidth = textContain.getWidth(text, computedFont);
+    var outerWidth = textWidth;
+    textPadding && (outerWidth += textPadding[1] + textPadding[3]);
+    var boxX = textContain.adjustTextX(baseX, outerWidth, textAlign);
+    needDrawBg && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
+
+    if (textPadding) {
+      textX = getTextXForPadding(baseX, textAlign, textPadding);
+      textY += textPadding[0];
+    }
+  } // Always set textAlign and textBase line, because it is difficute to calculate
+  // textAlign from prevEl, and we dont sure whether textAlign will be reset if
+  // font set happened.
+
+
+  ctx.textAlign = textAlign; // Force baseline to be "middle". Otherwise, if using "top", the
+  // text will offset downward a little bit in font "Microsoft YaHei".
+
+  ctx.textBaseline = 'middle'; // Set text opacity
+
+  ctx.globalAlpha = style.opacity || 1; // Always set shadowBlur and shadowOffset to avoid leak from displayable.
+
+  for (var i = 0; i < SHADOW_STYLE_COMMON_PROPS.length; i++) {
+    var propItem = SHADOW_STYLE_COMMON_PROPS[i];
+    var styleProp = propItem[0];
+    var ctxProp = propItem[1];
+    var val = style[styleProp];
+
+    if (!checkCache || val !== prevStyle[styleProp]) {
+      ctx[ctxProp] = fixShadow(ctx, ctxProp, val || propItem[2]);
+    }
+  } // `textBaseline` is set as 'middle'.
+
+
+  textY += lineHeight / 2;
+  var textStrokeWidth = style.textStrokeWidth;
+  var textStrokeWidthPrev = checkCache ? prevStyle.textStrokeWidth : null;
+  var strokeWidthChanged = !checkCache || textStrokeWidth !== textStrokeWidthPrev;
+  var strokeChanged = !checkCache || strokeWidthChanged || style.textStroke !== prevStyle.textStroke;
+  var textStroke = getStroke(style.textStroke, textStrokeWidth);
+  var textFill = getFill(style.textFill);
+
+  if (textStroke) {
+    if (strokeWidthChanged) {
+      ctx.lineWidth = textStrokeWidth;
+    }
+
+    if (strokeChanged) {
+      ctx.strokeStyle = textStroke;
+    }
+  }
+
+  if (textFill) {
+    if (!checkCache || style.textFill !== prevStyle.textFill) {
+      ctx.fillStyle = textFill;
+    }
+  } // Optimize simply, in most cases only one line exists.
+
+
+  if (textLines.length === 1) {
+    // Fill after stroke so the outline will not cover the main part.
+    textStroke && ctx.strokeText(textLines[0], textX, textY);
+    textFill && ctx.fillText(textLines[0], textX, textY);
+  } else {
+    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], textX, textY);
+      textFill && ctx.fillText(textLines[i], textX, textY);
+      textY += lineHeight;
+    }
+  }
+}
+
+function renderRichText(hostEl, ctx, text, style, rect, prevEl) {
+  // Do not do cache for rich text because of the complexity.
+  // But `RectText` this will be restored, do not need to clear other cache like `Style::bind`.
+  if (prevEl !== WILL_BE_RESTORED) {
+    ctx.__attrCachedBy = ContextCachedBy.NONE;
+  }
+
+  var contentBlock = hostEl.__textCotentBlock;
+
+  if (!contentBlock || hostEl.__dirtyText) {
+    contentBlock = hostEl.__textCotentBlock = textContain.parseRichText(text, style);
+  }
+
+  drawRichText(hostEl, ctx, contentBlock, style, rect);
+}
+
+function drawRichText(hostEl, ctx, contentBlock, style, rect) {
+  var contentWidth = contentBlock.width;
+  var outerWidth = contentBlock.outerWidth;
+  var outerHeight = contentBlock.outerHeight;
+  var textPadding = style.textPadding;
+  var boxPos = getBoxPosition(_tmpBoxPositionResult, hostEl, style, rect);
+  var baseX = boxPos.baseX;
+  var baseY = boxPos.baseY;
+  var textAlign = boxPos.textAlign;
+  var textVerticalAlign = boxPos.textVerticalAlign; // Origin of textRotation should be the base point of text drawing.
+
+  applyTextRotation(ctx, style, rect, baseX, baseY);
+  var boxX = textContain.adjustTextX(baseX, outerWidth, textAlign);
+  var boxY = textContain.adjustTextY(baseY, outerHeight, textVerticalAlign);
+  var xLeft = boxX;
+  var lineTop = boxY;
+
+  if (textPadding) {
+    xLeft += textPadding[3];
+    lineTop += textPadding[0];
+  }
+
+  var xRight = xLeft + contentWidth;
+  needDrawBackground(style) && drawBackground(hostEl, ctx, style, boxX, boxY, outerWidth, outerHeight);
+
+  for (var i = 0; i < contentBlock.lines.length; i++) {
+    var line = contentBlock.lines[i];
+    var tokens = line.tokens;
+    var tokenCount = tokens.length;
+    var lineHeight = line.lineHeight;
+    var usedWidth = line.width;
+    var leftIndex = 0;
+    var lineXLeft = xLeft;
+    var lineXRight = xRight;
+    var rightIndex = tokenCount - 1;
+    var token;
+
+    while (leftIndex < tokenCount && (token = tokens[leftIndex], !token.textAlign || token.textAlign === 'left')) {
+      placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft, 'left');
+      usedWidth -= token.width;
+      lineXLeft += token.width;
+      leftIndex++;
+    }
+
+    while (rightIndex >= 0 && (token = tokens[rightIndex], token.textAlign === 'right')) {
+      placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXRight, 'right');
+      usedWidth -= token.width;
+      lineXRight -= token.width;
+      rightIndex--;
+    } // The other tokens are placed as textAlign 'center' if there is enough space.
+
+
+    lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - usedWidth) / 2;
+
+    while (leftIndex <= rightIndex) {
+      token = tokens[leftIndex]; // Consider width specified by user, use 'center' rather than 'left'.
+
+      placeToken(hostEl, ctx, token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center');
+      lineXLeft += token.width;
+      leftIndex++;
+    }
+
+    lineTop += lineHeight;
+  }
+}
+
+function applyTextRotation(ctx, style, rect, x, y) {
+  // textRotation only apply in RectText.
+  if (rect && style.textRotation) {
+    var origin = style.textOrigin;
+
+    if (origin === 'center') {
+      x = rect.width / 2 + rect.x;
+      y = rect.height / 2 + rect.y;
+    } else if (origin) {
+      x = origin[0] + rect.x;
+      y = origin[1] + rect.y;
+    }
+
+    ctx.translate(x, y); // Positive: anticlockwise
+
+    ctx.rotate(-style.textRotation);
+    ctx.translate(-x, -y);
+  }
+}
+
+function placeToken(hostEl, ctx, token, style, lineHeight, lineTop, x, textAlign) {
+  var tokenStyle = style.rich[token.styleName] || {};
+  tokenStyle.text = token.text; // 'ctx.textBaseline' is always set as 'middle', for sake of
+  // the bias of "Microsoft YaHei".
+
+  var textVerticalAlign = token.textVerticalAlign;
+  var y = lineTop + lineHeight / 2;
+
+  if (textVerticalAlign === 'top') {
+    y = lineTop + token.height / 2;
+  } else if (textVerticalAlign === 'bottom') {
+    y = lineTop + lineHeight - token.height / 2;
+  }
+
+  !token.isLineHolder && needDrawBackground(tokenStyle) && drawBackground(hostEl, ctx, tokenStyle, textAlign === 'right' ? x - token.width : textAlign === 'center' ? x - token.width / 2 : x, y - token.height / 2, token.width, token.height);
+  var textPadding = token.textPadding;
+
+  if (textPadding) {
+    x = getTextXForPadding(x, textAlign, textPadding);
+    y -= token.height / 2 - textPadding[2] - token.textHeight / 2;
+  }
+
+  setCtx(ctx, 'shadowBlur', retrieve3(tokenStyle.textShadowBlur, style.textShadowBlur, 0));
+  setCtx(ctx, 'shadowColor', tokenStyle.textShadowColor || style.textShadowColor || 'transparent');
+  setCtx(ctx, 'shadowOffsetX', retrieve3(tokenStyle.textShadowOffsetX, style.textShadowOffsetX, 0));
+  setCtx(ctx, 'shadowOffsetY', retrieve3(tokenStyle.textShadowOffsetY, style.textShadowOffsetY, 0));
+  setCtx(ctx, 'textAlign', textAlign); // Force baseline to be "middle". Otherwise, if using "top", the
+  // text will offset downward a little bit in font "Microsoft YaHei".
+
+  setCtx(ctx, 'textBaseline', 'middle');
+  setCtx(ctx, 'font', token.font || DEFAULT_FONT);
+  var textStroke = getStroke(tokenStyle.textStroke || style.textStroke, textStrokeWidth);
+  var textFill = getFill(tokenStyle.textFill || style.textFill);
+  var textStrokeWidth = retrieve2(tokenStyle.textStrokeWidth, style.textStrokeWidth); // Fill after stroke so the outline will not cover the main part.
+
+  if (textStroke) {
+    setCtx(ctx, 'lineWidth', textStrokeWidth);
+    setCtx(ctx, 'strokeStyle', textStroke);
+    ctx.strokeText(token.text, x, y);
+  }
+
+  if (textFill) {
+    setCtx(ctx, 'fillStyle', textFill);
+    ctx.fillText(token.text, x, y);
+  }
+}
+
+function needDrawBackground(style) {
+  return !!(style.textBackgroundColor || style.textBorderWidth && style.textBorderColor);
+} // style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius, text}
+// shape: {x, y, width, height}
+
+
+function drawBackground(hostEl, ctx, style, x, y, width, height) {
+  var textBackgroundColor = style.textBackgroundColor;
+  var textBorderWidth = style.textBorderWidth;
+  var textBorderColor = style.textBorderColor;
+  var isPlainBg = isString(textBackgroundColor);
+  setCtx(ctx, 'shadowBlur', style.textBoxShadowBlur || 0);
+  setCtx(ctx, 'shadowColor', style.textBoxShadowColor || 'transparent');
+  setCtx(ctx, 'shadowOffsetX', style.textBoxShadowOffsetX || 0);
+  setCtx(ctx, 'shadowOffsetY', style.textBoxShadowOffsetY || 0);
+
+  if (isPlainBg || textBorderWidth && textBorderColor) {
+    ctx.beginPath();
+    var textBorderRadius = style.textBorderRadius;
+
+    if (!textBorderRadius) {
+      ctx.rect(x, y, width, height);
+    } else {
+      roundRectHelper.buildPath(ctx, {
+        x: x,
+        y: y,
+        width: width,
+        height: height,
+        r: textBorderRadius
+      });
+    }
+
+    ctx.closePath();
+  }
+
+  if (isPlainBg) {
+    setCtx(ctx, 'fillStyle', textBackgroundColor);
+
+    if (style.fillOpacity != null) {
+      var originalGlobalAlpha = ctx.globalAlpha;
+      ctx.globalAlpha = style.fillOpacity * style.opacity;
+      ctx.fill();
+      ctx.globalAlpha = originalGlobalAlpha;
+    } else {
+      ctx.fill();
+    }
+  } else if (isObject(textBackgroundColor)) {
+    var image = textBackgroundColor.image;
+    image = imageHelper.createOrUpdateImage(image, null, hostEl, onBgImageLoaded, textBackgroundColor);
+
+    if (image && imageHelper.isImageReady(image)) {
+      ctx.drawImage(image, x, y, width, height);
+    }
+  }
+
+  if (textBorderWidth && textBorderColor) {
+    setCtx(ctx, 'lineWidth', textBorderWidth);
+    setCtx(ctx, 'strokeStyle', textBorderColor);
+
+    if (style.strokeOpacity != null) {
+      var originalGlobalAlpha = ctx.globalAlpha;
+      ctx.globalAlpha = style.strokeOpacity * style.opacity;
+      ctx.stroke();
+      ctx.globalAlpha = originalGlobalAlpha;
+    } else {
+      ctx.stroke();
+    }
+  }
+}
+
+function onBgImageLoaded(image, textBackgroundColor) {
+  // Replace image, so that `contain/text.js#parseRichText`
+  // will get correct result in next tick.
+  textBackgroundColor.image = image;
+}
+
+function getBoxPosition(out, hostEl, style, rect) {
+  var baseX = style.x || 0;
+  var baseY = style.y || 0;
+  var textAlign = style.textAlign;
+  var textVerticalAlign = style.textVerticalAlign; // Text position represented by coord
+
+  if (rect) {
+    var textPosition = style.textPosition;
+
+    if (textPosition instanceof Array) {
+      // Percent
+      baseX = rect.x + parsePercent(textPosition[0], rect.width);
+      baseY = rect.y + parsePercent(textPosition[1], rect.height);
+    } else {
+      var res = hostEl && hostEl.calculateTextPosition ? hostEl.calculateTextPosition(_tmpTextPositionResult, style, rect) : textContain.calculateTextPosition(_tmpTextPositionResult, style, rect);
+      baseX = res.x;
+      baseY = res.y; // Default align and baseline when has textPosition
+
+      textAlign = textAlign || res.textAlign;
+      textVerticalAlign = textVerticalAlign || res.textVerticalAlign;
+    } // textOffset is only support in RectText, otherwise
+    // we have to adjust boundingRect for textOffset.
+
+
+    var textOffset = style.textOffset;
+
+    if (textOffset) {
+      baseX += textOffset[0];
+      baseY += textOffset[1];
+    }
+  }
+
+  out = out || {};
+  out.baseX = baseX;
+  out.baseY = baseY;
+  out.textAlign = textAlign;
+  out.textVerticalAlign = textVerticalAlign;
+  return out;
+}
+
+function setCtx(ctx, prop, value) {
+  ctx[prop] = fixShadow(ctx, prop, value);
+  return ctx[prop];
+}
+/**
+ * @param {string} [stroke] If specified, do not check style.textStroke.
+ * @param {string} [lineWidth] If specified, do not check style.textStroke.
+ * @param {number} style
+ */
+
+
+function getStroke(stroke, lineWidth) {
+  return stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none' ? null // TODO pattern and gradient?
+  : stroke.image || stroke.colorStops ? '#000' : stroke;
+}
+
+function getFill(fill) {
+  return fill == null || fill === 'none' ? null // TODO pattern and gradient?
+  : fill.image || fill.colorStops ? '#000' : fill;
+}
+
+function parsePercent(value, maxValue) {
+  if (typeof value === 'string') {
+    if (value.lastIndexOf('%') >= 0) {
+      return parseFloat(value) / 100 * maxValue;
+    }
+
+    return parseFloat(value);
+  }
+
+  return value;
+}
+
+function getTextXForPadding(x, textAlign, textPadding) {
+  return textAlign === 'right' ? x - textPadding[1] : textAlign === 'center' ? x + textPadding[3] / 2 - textPadding[1] / 2 : x + textPadding[3];
+}
+/**
+ * @param {string} text
+ * @param {module:zrender/Style} style
+ * @return {boolean}
+ */
+
+
+function needDrawText(text, style) {
+  return text != null && (text || style.textBackgroundColor || style.textBorderWidth && style.textBorderColor || style.textPadding);
+}
+
+exports.normalizeTextStyle = normalizeTextStyle;
+exports.renderText = renderText;
+exports.getBoxPosition = getBoxPosition;
+exports.getStroke = getStroke;
+exports.getFill = getFill;
+exports.parsePercent = parsePercent;
+exports.needDrawText = needDrawText;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/mixin/RectText.js":
+/*!************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/mixin/RectText.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var textHelper = __webpack_require__(/*! ../helper/text */ "./node_modules/zrender/lib/graphic/helper/text.js");
+
+var BoundingRect = __webpack_require__(/*! ../../core/BoundingRect */ "./node_modules/zrender/lib/core/BoundingRect.js");
+
+var _constant = __webpack_require__(/*! ../constant */ "./node_modules/zrender/lib/graphic/constant.js");
+
+var WILL_BE_RESTORED = _constant.WILL_BE_RESTORED;
+
+/**
+ * Mixin for drawing text in a element bounding rect
+ * @module zrender/mixin/RectText
+ */
+var tmpRect = new BoundingRect();
+
+var RectText = function () {};
+
+RectText.prototype = {
+  constructor: RectText,
+
+  /**
+   * Draw text in a rect with specified position.
+   * @param  {CanvasRenderingContext2D} ctx
+   * @param  {Object} rect Displayable rect
+   */
+  drawRectText: function (ctx, rect) {
+    var style = this.style;
+    rect = style.textRect || rect; // Optimize, avoid normalize every time.
+
+    this.__dirty && textHelper.normalizeTextStyle(style, true);
+    var text = style.text; // Convert to string
+
+    text != null && (text += '');
+
+    if (!textHelper.needDrawText(text, style)) {
+      return;
+    } // FIXME
+    // Do not provide prevEl to `textHelper.renderText` for ctx prop cache,
+    // but use `ctx.save()` and `ctx.restore()`. Because the cache for rect
+    // text propably break the cache for its host elements.
+
+
+    ctx.save(); // Transform rect to view space
+
+    var transform = this.transform;
+
+    if (!style.transformText) {
+      if (transform) {
+        tmpRect.copy(rect);
+        tmpRect.applyTransform(transform);
+        rect = tmpRect;
+      }
+    } else {
+      this.setTransform(ctx);
+    } // transformText and textRotation can not be used at the same time.
+
+
+    textHelper.renderText(this, ctx, text, style, rect, WILL_BE_RESTORED);
+    ctx.restore();
+  }
+};
+var _default = RectText;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Arc.js":
+/*!*******************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Arc.js ***!
+  \*******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+/**
+ * 圆弧
+ * @module zrender/graphic/shape/Arc
+ */
+var _default = Path.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);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/BezierCurve.js":
+/*!***************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/BezierCurve.js ***!
+  \***************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var vec2 = __webpack_require__(/*! ../../core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var _curve = __webpack_require__(/*! ../../core/curve */ "./node_modules/zrender/lib/core/curve.js");
+
+var quadraticSubdivide = _curve.quadraticSubdivide;
+var cubicSubdivide = _curve.cubicSubdivide;
+var quadraticAt = _curve.quadraticAt;
+var cubicAt = _curve.cubicAt;
+var quadraticDerivativeAt = _curve.quadraticDerivativeAt;
+var cubicDerivativeAt = _curve.cubicDerivativeAt;
+
+/**
+ * 贝塞尔曲线
+ * @module zrender/shape/BezierCurve
+ */
+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)];
+  }
+}
+
+var _default = Path.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);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Circle.js":
+/*!**********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Circle.js ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+/**
+ * 圆形
+ * @module zrender/shape/Circle
+ */
+var _default = Path.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);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Line.js":
+/*!********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Line.js ***!
+  \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var _subPixelOptimize = __webpack_require__(/*! ../helper/subPixelOptimize */ "./node_modules/zrender/lib/graphic/helper/subPixelOptimize.js");
+
+var subPixelOptimizeLine = _subPixelOptimize.subPixelOptimizeLine;
+
+/**
+ * 直线
+ * @module zrender/graphic/shape/Line
+ */
+// Avoid create repeatly.
+var subPixelOptimizeOutputShape = {};
+
+var _default = Path.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;
+    var y1;
+    var x2;
+    var y2;
+
+    if (this.subPixelOptimize) {
+      subPixelOptimizeLine(subPixelOptimizeOutputShape, shape, this.style);
+      x1 = subPixelOptimizeOutputShape.x1;
+      y1 = subPixelOptimizeOutputShape.y1;
+      x2 = subPixelOptimizeOutputShape.x2;
+      y2 = subPixelOptimizeOutputShape.y2;
+    } else {
+      x1 = shape.x1;
+      y1 = shape.y1;
+      x2 = shape.x2;
+      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];
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Polygon.js":
+/*!***********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Polygon.js ***!
+  \***********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var polyHelper = __webpack_require__(/*! ../helper/poly */ "./node_modules/zrender/lib/graphic/helper/poly.js");
+
+/**
+ * 多边形
+ * @module zrender/shape/Polygon
+ */
+var _default = Path.extend({
+  type: 'polygon',
+  shape: {
+    points: null,
+    smooth: false,
+    smoothConstraint: null
+  },
+  buildPath: function (ctx, shape) {
+    polyHelper.buildPath(ctx, shape, true);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Polyline.js":
+/*!************************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Polyline.js ***!
+  \************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var polyHelper = __webpack_require__(/*! ../helper/poly */ "./node_modules/zrender/lib/graphic/helper/poly.js");
+
+/**
+ * @module zrender/graphic/shape/Polyline
+ */
+var _default = Path.extend({
+  type: 'polyline',
+  shape: {
+    points: null,
+    smooth: false,
+    smoothConstraint: null
+  },
+  style: {
+    stroke: '#000',
+    fill: null
+  },
+  buildPath: function (ctx, shape) {
+    polyHelper.buildPath(ctx, shape, false);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Rect.js":
+/*!********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Rect.js ***!
+  \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var roundRectHelper = __webpack_require__(/*! ../helper/roundRect */ "./node_modules/zrender/lib/graphic/helper/roundRect.js");
+
+var _subPixelOptimize = __webpack_require__(/*! ../helper/subPixelOptimize */ "./node_modules/zrender/lib/graphic/helper/subPixelOptimize.js");
+
+var subPixelOptimizeRect = _subPixelOptimize.subPixelOptimizeRect;
+
+/**
+ * 矩形
+ * @module zrender/graphic/shape/Rect
+ */
+// Avoid create repeatly.
+var subPixelOptimizeOutputShape = {};
+
+var _default = Path.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;
+    var y;
+    var width;
+    var height;
+
+    if (this.subPixelOptimize) {
+      subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style);
+      x = subPixelOptimizeOutputShape.x;
+      y = subPixelOptimizeOutputShape.y;
+      width = subPixelOptimizeOutputShape.width;
+      height = subPixelOptimizeOutputShape.height;
+      subPixelOptimizeOutputShape.r = shape.r;
+      shape = subPixelOptimizeOutputShape;
+    } else {
+      x = shape.x;
+      y = shape.y;
+      width = shape.width;
+      height = shape.height;
+    }
+
+    if (!shape.r) {
+      ctx.rect(x, y, width, height);
+    } else {
+      roundRectHelper.buildPath(ctx, shape);
+    }
+
+    ctx.closePath();
+    return;
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Ring.js":
+/*!********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Ring.js ***!
+  \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+/**
+ * 圆环
+ * @module zrender/graphic/shape/Ring
+ */
+var _default = Path.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);
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/graphic/shape/Sector.js":
+/*!**********************************************************!*\
+  !*** ./node_modules/zrender/lib/graphic/shape/Sector.js ***!
+  \**********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var fixClipWithShadow = __webpack_require__(/*! ../helper/fixClipWithShadow */ "./node_modules/zrender/lib/graphic/helper/fixClipWithShadow.js");
+
+/**
+ * 扇形
+ * @module zrender/graphic/shape/Sector
+ */
+var _default = Path.extend({
+  type: 'sector',
+  shape: {
+    cx: 0,
+    cy: 0,
+    r0: 0,
+    r: 0,
+    startAngle: 0,
+    endAngle: Math.PI * 2,
+    clockwise: true
+  },
+  brush: fixClipWithShadow(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();
+  }
+});
+
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/mixin/Animatable.js":
+/*!******************************************************!*\
+  !*** ./node_modules/zrender/lib/mixin/Animatable.js ***!
+  \******************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Animator = __webpack_require__(/*! ../animation/Animator */ "./node_modules/zrender/lib/animation/Animator.js");
+
+var logError = __webpack_require__(/*! ../core/log */ "./node_modules/zrender/lib/core/log.js");
+
+var _util = __webpack_require__(/*! ../core/util */ "./node_modules/zrender/lib/core/util.js");
+
+var isString = _util.isString;
+var isFunction = _util.isFunction;
+var isObject = _util.isObject;
+var isArrayLike = _util.isArrayLike;
+var indexOf = _util.indexOf;
+
+/**
+ * @alias module:zrender/mixin/Animatable
+ * @constructor
+ */
+var Animatable = function () {
+  /**
+   * @type {Array.<module:zrender/animation/Animator>}
+   * @readOnly
+   */
+  this.animators = [];
+};
+
+Animatable.prototype = {
+  constructor: Animatable,
+
+  /**
+   * 动画
+   *
+   * @param {string} path The path to fetch value from object, like 'a.b.c'.
+   * @param {boolean} [loop] Whether to loop animation.
+   * @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) {
+      logError('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(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;
+  },
+
+  /**
+   * Caution: this method will stop previous animation.
+   * So do not use this method to one element twice before
+   * animation starts, unless you know what you are doing.
+   * @param {Object} target
+   * @param {number} [time=500] Time in ms
+   * @param {string} [easing='linear']
+   * @param {number} [delay=0]
+   * @param {Function} [callback]
+   * @param {Function} [forceAnimate] Prevent stop animation and callback
+   *        immediently when target values are the same as current values.
+   *
+   * @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, forceAnimate) {
+    animateTo(this, target, time, delay, easing, callback, forceAnimate);
+  },
+
+  /**
+   * Animate from the target state to current state.
+   * The params and the return value are the same as `this.animateTo`.
+   */
+  animateFrom: function (target, time, delay, easing, callback, forceAnimate) {
+    animateTo(this, target, time, delay, easing, callback, forceAnimate, true);
+  }
+};
+
+function animateTo(animatable, target, time, delay, easing, callback, forceAnimate, reverse) {
+  // 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
+
+
+  animatable.stopAnimation();
+  animateToShallow(animatable, '', animatable, target, time, delay, reverse); // Animators may be removed immediately after start
+  // if there is nothing to animate
+
+  var animators = animatable.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, forceAnimate);
+  }
+}
+/**
+ * @param {string} path=''
+ * @param {Object} source=animatable
+ * @param {Object} target
+ * @param {number} [time=500]
+ * @param {number} [delay=0]
+ * @param {boolean} [reverse] If `true`, animate
+ *        from the `target` to current state.
+ *
+ * @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)
+ */
+
+
+function animateToShallow(animatable, path, source, target, time, delay, reverse) {
+  var objShallow = {};
+  var propertyCount = 0;
+
+  for (var name in target) {
+    if (!target.hasOwnProperty(name)) {
+      continue;
+    }
+
+    if (source[name] != null) {
+      if (isObject(target[name]) && !isArrayLike(target[name])) {
+        animateToShallow(animatable, path ? path + '.' + name : name, source[name], target[name], time, delay, reverse);
+      } else {
+        if (reverse) {
+          objShallow[name] = source[name];
+          setAttrByPath(animatable, path, name, target[name]);
+        } else {
+          objShallow[name] = target[name];
+        }
+
+        propertyCount++;
+      }
+    } else if (target[name] != null && !reverse) {
+      setAttrByPath(animatable, path, name, target[name]);
+    }
+  }
+
+  if (propertyCount > 0) {
+    animatable.animate(path, false).when(time == null ? 500 : time, objShallow).delay(delay || 0);
+  }
+}
+
+function setAttrByPath(el, path, name, value) {
+  // Attr directly if not has property
+  // FIXME, if some property not needed for element ?
+  if (!path) {
+    el.attr(name, value);
+  } else {
+    // Only support set shape or style
+    var props = {};
+    props[path] = {};
+    props[path][name] = value;
+    el.attr(props);
+  }
+}
+
+var _default = Animatable;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/mixin/Eventful.js":
+/*!****************************************************!*\
+  !*** ./node_modules/zrender/lib/mixin/Eventful.js ***!
+  \****************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+/**
+ * Event Mixin
+ * @module zrender/mixin/Eventful
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ *         pissang (https://www.github.com/pissang)
+ */
+var arrySlice = Array.prototype.slice;
+/**
+ * Event dispatcher.
+ *
+ * @alias module:zrender/mixin/Eventful
+ * @constructor
+ * @param {Object} [eventProcessor] The object eventProcessor is the scope when
+ *        `eventProcessor.xxx` called.
+ * @param {Function} [eventProcessor.normalizeQuery]
+ *        param: {string|Object} Raw query.
+ *        return: {string|Object} Normalized query.
+ * @param {Function} [eventProcessor.filter] Event will be dispatched only
+ *        if it returns `true`.
+ *        param: {string} eventType
+ *        param: {string|Object} query
+ *        return: {boolean}
+ * @param {Function} [eventProcessor.afterTrigger] Called after all handlers called.
+ *        param: {string} eventType
+ */
+
+var Eventful = function (eventProcessor) {
+  this._$handlers = {};
+  this._$eventProcessor = eventProcessor;
+};
+
+Eventful.prototype = {
+  constructor: Eventful,
+
+  /**
+   * The handler can only be triggered once, then removed.
+   *
+   * @param {string} event The event name.
+   * @param {string|Object} [query] Condition used on event filter.
+   * @param {Function} handler The event handler.
+   * @param {Object} context
+   */
+  one: function (event, query, handler, context) {
+    return on(this, event, query, handler, context, true);
+  },
+
+  /**
+   * Bind a handler.
+   *
+   * @param {string} event The event name.
+   * @param {string|Object} [query] Condition used on event filter.
+   * @param {Function} handler The event handler.
+   * @param {Object} [context]
+   */
+  on: function (event, query, handler, context) {
+    return on(this, event, query, handler, context, false);
+  },
+
+  /**
+   * Whether any handler has bound.
+   *
+   * @param  {string}  event
+   * @return {boolean}
+   */
+  isSilent: function (event) {
+    var _h = this._$handlers;
+    return !_h[event] || !_h[event].length;
+  },
+
+  /**
+   * Unbind a event.
+   *
+   * @param {string} [event] The event name.
+   *        If no `event` input, "off" all listeners.
+   * @param {Function} [handler] The event handler.
+   *        If no `handler` input, "off" all listeners of the `event`.
+   */
+  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;
+  },
+
+  /**
+   * Dispatch a event.
+   *
+   * @param {string} type The event name.
+   */
+  trigger: function (type) {
+    var _h = this._$handlers[type];
+    var eventProcessor = this._$eventProcessor;
+
+    if (_h) {
+      var args = arguments;
+      var argLen = args.length;
+
+      if (argLen > 3) {
+        args = arrySlice.call(args, 1);
+      }
+
+      var len = _h.length;
+
+      for (var i = 0; i < len;) {
+        var hItem = _h[i];
+
+        if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) {
+          i++;
+          continue;
+        } // Optimize advise from backbone
+
+
+        switch (argLen) {
+          case 1:
+            hItem.h.call(hItem.ctx);
+            break;
+
+          case 2:
+            hItem.h.call(hItem.ctx, args[1]);
+            break;
+
+          case 3:
+            hItem.h.call(hItem.ctx, args[1], args[2]);
+            break;
+
+          default:
+            // have more than 2 given arguments
+            hItem.h.apply(hItem.ctx, args);
+            break;
+        }
+
+        if (hItem.one) {
+          _h.splice(i, 1);
+
+          len--;
+        } else {
+          i++;
+        }
+      }
+    }
+
+    eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type);
+    return this;
+  },
+
+  /**
+   * Dispatch a event with context, which is specified at the last parameter.
+   *
+   * @param {string} type The event name.
+   */
+  triggerWithContext: function (type) {
+    var _h = this._$handlers[type];
+    var eventProcessor = this._$eventProcessor;
+
+    if (_h) {
+      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 len = _h.length;
+
+      for (var i = 0; i < len;) {
+        var hItem = _h[i];
+
+        if (eventProcessor && eventProcessor.filter && hItem.query != null && !eventProcessor.filter(type, hItem.query)) {
+          i++;
+          continue;
+        } // Optimize advise from backbone
+
+
+        switch (argLen) {
+          case 1:
+            hItem.h.call(ctx);
+            break;
+
+          case 2:
+            hItem.h.call(ctx, args[1]);
+            break;
+
+          case 3:
+            hItem.h.call(ctx, args[1], args[2]);
+            break;
+
+          default:
+            // have more than 2 given arguments
+            hItem.h.apply(ctx, args);
+            break;
+        }
+
+        if (hItem.one) {
+          _h.splice(i, 1);
+
+          len--;
+        } else {
+          i++;
+        }
+      }
+    }
+
+    eventProcessor && eventProcessor.afterTrigger && eventProcessor.afterTrigger(type);
+    return this;
+  }
+};
+
+function normalizeQuery(host, query) {
+  var eventProcessor = host._$eventProcessor;
+
+  if (query != null && eventProcessor && eventProcessor.normalizeQuery) {
+    query = eventProcessor.normalizeQuery(query);
+  }
+
+  return query;
+}
+
+function on(eventful, event, query, handler, context, isOnce) {
+  var _h = eventful._$handlers;
+
+  if (typeof query === 'function') {
+    context = handler;
+    handler = query;
+    query = null;
+  }
+
+  if (!handler || !event) {
+    return eventful;
+  }
+
+  query = normalizeQuery(eventful, query);
+
+  if (!_h[event]) {
+    _h[event] = [];
+  }
+
+  for (var i = 0; i < _h[event].length; i++) {
+    if (_h[event][i].h === handler) {
+      return eventful;
+    }
+  }
+
+  var wrap = {
+    h: handler,
+    one: isOnce,
+    query: query,
+    ctx: context || eventful,
+    // FIXME
+    // Do not publish this feature util it is proved that it makes sense.
+    callAtLast: handler.zrEventfulCallAtLast
+  };
+  var lastIndex = _h[event].length - 1;
+  var lastWrap = _h[event][lastIndex];
+  lastWrap && lastWrap.callAtLast ? _h[event].splice(lastIndex, 0, wrap) : _h[event].push(wrap);
+  return eventful;
+} // ----------------------
+// The events in zrender
+// ----------------------
+
+/**
+ * @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
+ */
+
+
+var _default = Eventful;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/mixin/Transformable.js":
+/*!*********************************************************!*\
+  !*** ./node_modules/zrender/lib/mixin/Transformable.js ***!
+  \*********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var matrix = __webpack_require__(/*! ../core/matrix */ "./node_modules/zrender/lib/core/matrix.js");
+
+var vector = __webpack_require__(/*! ../core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+/**
+ * 提供变换扩展
+ * @module zrender/mixin/Transformable
+ * @author pissang (https://www.github.com/pissang)
+ */
+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);
+};
+
+var scaleTmp = [];
+
+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;
+  var globalScaleRatio = this.globalScaleRatio;
+
+  if (globalScaleRatio != null && globalScaleRatio !== 1) {
+    this.getGlobalScale(scaleTmp);
+    var relX = scaleTmp[0] < 0 ? -1 : 1;
+    var relY = scaleTmp[1] < 0 ? -1 : 1;
+    var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;
+    var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;
+    m[0] *= sx;
+    m[1] *= sx;
+    m[2] *= sy;
+    m[3] *= sy;
+  }
+
+  this.invTransform = this.invTransform || matrix.create();
+  matrix.invert(this.invTransform, m);
+};
+
+transformableProto.getLocalTransform = function (m) {
+  return Transformable.getLocalTransform(this, m);
+};
+/**
+ * 将自己的transform应用到context上
+ * @param {CanvasRenderingContext2D} 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 = [];
+var originTransform = matrix.create();
+
+transformableProto.setLocalTransform = function (m) {
+  if (!m) {
+    // TODO return or set identity?
+    return;
+  }
+
+  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);
+};
+/**
+ * 分解`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 origin = this.origin;
+
+  if (origin && (origin[0] || origin[1])) {
+    originTransform[4] = origin[0];
+    originTransform[5] = origin[1];
+    matrix.mul(tmpTransform, m, originTransform);
+    tmpTransform[4] -= origin[0];
+    tmpTransform[5] -= origin[1];
+    m = tmpTransform;
+  }
+
+  this.setLocalTransform(m);
+};
+/**
+ * Get global scale
+ * @return {Array.<number>}
+ */
+
+
+transformableProto.getGlobalScale = function (out) {
+  var m = this.transform;
+  out = out || [];
+
+  if (!m) {
+    out[0] = 1;
+    out[1] = 1;
+    return out;
+  }
+
+  out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
+  out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
+
+  if (m[0] < 0) {
+    out[0] = -out[0];
+  }
+
+  if (m[3] < 0) {
+    out[1] = -out[1];
+  }
+
+  return out;
+};
+/**
+ * 变换坐标位置到 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;
+};
+
+var _default = Transformable;
+module.exports = _default;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/tool/color.js":
+/*!************************************************!*\
+  !*** ./node_modules/zrender/lib/tool/color.js ***!
+  \************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var LRU = __webpack_require__(/*! ../core/LRU */ "./node_modules/zrender/lib/core/LRU.js");
+
+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 lerpNumber(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('(');
+  var 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;
+      }
+
+      if (colorArr[i] > 255) {
+        colorArr[i] = 255;
+      } else if (color[i] < 0) {
+        colorArr[i] = 0;
+      }
+    }
+
+    return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
+  }
+}
+/**
+ * @param {string} color
+ * @return {string}
+ * @memberOf module:zrender/util/color
+ */
+
+
+function toHex(color) {
+  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 lerp 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 fastLerp(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(lerpNumber(leftColor[0], rightColor[0], dv));
+  out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));
+  out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));
+  out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));
+  return out;
+}
+/**
+ * @deprecated
+ */
+
+
+var fastMapToColor = fastLerp;
+/**
+ * @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 lerp(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(lerpNumber(leftColor[0], rightColor[0], dv)), clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))], 'rgba');
+  return fullOutput ? {
+    color: color,
+    leftIndex: leftIndex,
+    rightIndex: rightIndex,
+    value: value
+  } : color;
+}
+/**
+ * @deprecated
+ */
+
+
+var mapToColor = lerp;
+/**
+ * @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 + ')';
+}
+
+exports.parse = parse;
+exports.lift = lift;
+exports.toHex = toHex;
+exports.fastLerp = fastLerp;
+exports.fastMapToColor = fastMapToColor;
+exports.lerp = lerp;
+exports.mapToColor = mapToColor;
+exports.modifyHSL = modifyHSL;
+exports.modifyAlpha = modifyAlpha;
+exports.stringify = stringify;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/tool/path.js":
+/*!***********************************************!*\
+  !*** ./node_modules/zrender/lib/tool/path.js ***!
+  \***********************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var Path = __webpack_require__(/*! ../graphic/Path */ "./node_modules/zrender/lib/graphic/Path.js");
+
+var PathProxy = __webpack_require__(/*! ../core/PathProxy */ "./node_modules/zrender/lib/core/PathProxy.js");
+
+var transformPath = __webpack_require__(/*! ./transformPath */ "./node_modules/zrender/lib/tool/transformPath.js");
+
+// 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);
+}
+
+var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig; // Consider case:
+// (1) delimiter can be comma or space, where continuous commas
+// or spaces should be seen as one comma.
+// (2) value can be like:
+// '2e-4', 'l.5.9' (ignore 0), 'M-10-10', 'l-2.43e-1,34.9983',
+// 'l-.5E1,54', '121-23-44-11' (no delimiter)
+
+var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; // var valueSplitReg = /[\s,]+/;
+
+function createPathProxyFromString(data) {
+  if (!data) {
+    return new PathProxy();
+  } // var data = 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]);
+  // }
+  // data = data.replace(/-/g, ',-');
+  // create array
+  // var arr = cs.split('|');
+  // init context point
+
+
+  var cpx = 0;
+  var cpy = 0;
+  var subpathX = cpx;
+  var subpathY = cpy;
+  var prevCmd;
+  var path = new PathProxy();
+  var CMD = PathProxy.CMD; // commandReg.lastIndex = 0;
+  // var cmdResult;
+  // while ((cmdResult = commandReg.exec(data)) != null) {
+  //     var cmdStr = cmdResult[1];
+  //     var cmdContent = cmdResult[2];
+
+  var cmdList = data.match(commandReg);
+
+  for (var l = 0; l < cmdList.length; l++) {
+    var cmdText = cmdList[l];
+    var cmdStr = cmdText.charAt(0);
+    var cmd; // String#split is faster a little bit than String#replace or RegExp#exec.
+    // var p = cmdContent.split(valueSplitReg);
+    // var pLen = 0;
+    // for (var i = 0; i < p.length; i++) {
+    //     // '' and other invalid str => NaN
+    //     var val = parseFloat(p[i]);
+    //     !isNaN(val) && (p[pLen++] = val);
+    // }
+
+    var p = cmdText.match(numberReg) || [];
+    var pLen = p.length;
+
+    for (var i = 0; i < pLen; i++) {
+      p[i] = parseFloat(p[i]);
+    }
+
+    var off = 0;
+
+    while (off < pLen) {
+      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 (cmdStr) {
+        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);
+          subpathX = cpx;
+          subpathY = cpy;
+          cmdStr = 'l';
+          break;
+
+        case 'M':
+          cpx = p[off++];
+          cpy = p[off++];
+          cmd = CMD.M;
+          path.addData(cmd, cpx, cpy);
+          subpathX = cpx;
+          subpathY = cpy;
+          cmdStr = '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 (cmdStr === 'z' || cmdStr === 'Z') {
+      cmd = CMD.Z;
+      path.addData(cmd); // z may be in the middle of the path.
+
+      cpx = subpathX;
+      cpy = subpathY;
+    }
+
+    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;
+}
+/**
+ * Create a Path object from path string data
+ * http://www.w3.org/TR/SVG/paths.html#PathData
+ * @param  {Object} opts Other options
+ */
+
+
+function createFromString(str, opts) {
+  return new Path(createPathOptions(str, opts));
+}
+/**
+ * Create a Path class from path string data
+ * @param  {string} str
+ * @param  {Object} opts Other options
+ */
+
+
+function extendFromString(str, opts) {
+  return Path.extend(createPathOptions(str, opts));
+}
+/**
+ * Merge multiple paths
+ */
+// TODO Apply transform
+// TODO stroke dash
+// TODO Optimize double memory cost problem
+
+
+function mergePath(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;
+}
+
+exports.createFromString = createFromString;
+exports.extendFromString = extendFromString;
+exports.mergePath = mergePath;
+
+/***/ }),
+
+/***/ "./node_modules/zrender/lib/tool/transformPath.js":
+/*!********************************************************!*\
+  !*** ./node_modules/zrender/lib/tool/transformPath.js ***!
+  \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var PathProxy = __webpack_require__(/*! ../core/PathProxy */ "./node_modules/zrender/lib/core/PathProxy.js");
+
+var _vector = __webpack_require__(/*! ../core/vector */ "./node_modules/zrender/lib/core/vector.js");
+
+var v2ApplyTransform = _vector.applyTransform;
+var CMD = PathProxy.CMD;
+var points = [[], [], []];
+var mathSqrt = Math.sqrt;
+var mathAtan2 = Math.atan2;
+
+function _default(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 = _default;
+
+/***/ }),
+
+/***/ "./src/liquidFill.js":
+/*!***************************!*\
+  !*** ./src/liquidFill.js ***!
+  \***************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var echarts = __webpack_require__(/*! echarts/lib/echarts */ "echarts/lib/echarts");
+
+__webpack_require__(/*! ./liquidFillSeries */ "./src/liquidFillSeries.js");
+__webpack_require__(/*! ./liquidFillView */ "./src/liquidFillView.js");
+
+
+echarts.registerVisual(
+    echarts.util.curry(
+        __webpack_require__(/*! echarts/lib/visual/dataColor */ "./node_modules/echarts/lib/visual/dataColor.js"), 'liquidFill'
+    )
+);
+
+
+/***/ }),
+
+/***/ "./src/liquidFillLayout.js":
+/*!*********************************!*\
+  !*** ./src/liquidFillLayout.js ***!
+  \*********************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var echarts = __webpack_require__(/*! echarts/lib/echarts */ "echarts/lib/echarts");
+
+module.exports = echarts.graphic.extendShape({
+    type: 'ec-liquid-fill',
+
+    shape: {
+        waveLength: 0,
+        radius: 0,
+        radiusY: 0,
+        cx: 0,
+        cy: 0,
+        waterLevel: 0,
+        amplitude: 0,
+        phase: 0,
+        inverse: false
+    },
+
+    buildPath: function (ctx, shape) {
+        if (shape.radiusY == null) {
+            shape.radiusY = shape.radius;
+        }
+
+        /**
+         * We define a sine wave having 4 waves, and make sure at least 8 curves
+         * is drawn. Otherwise, it may cause blank area for some waves when
+         * wave length is large enough.
+         */
+        var curves = Math.max(
+            Math.ceil(2 * shape.radius / shape.waveLength * 4) * 2,
+            8
+        );
+
+        // map phase to [-Math.PI * 2, 0]
+        while (shape.phase < -Math.PI * 2) {
+            shape.phase += Math.PI * 2;
+        }
+        while (shape.phase > 0) {
+            shape.phase -= Math.PI * 2;
+        }
+        var phase = shape.phase / Math.PI / 2 * shape.waveLength;
+
+        var left = shape.cx - shape.radius + phase - shape.radius * 2;
+
+        /**
+         * top-left corner as start point
+         *
+         * draws this point
+         *  |
+         * \|/
+         *  ~~~~~~~~
+         *  |      |
+         *  +------+
+         */
+        ctx.moveTo(left, shape.waterLevel);
+
+        /**
+         * top wave
+         *
+         * ~~~~~~~~ <- draws this sine wave
+         * |      |
+         * +------+
+         */
+        var waveRight = 0;
+        for (var c = 0; c < curves; ++c) {
+            var stage = c % 4;
+            var pos = getWaterPositions(c * shape.waveLength / 4, stage,
+                shape.waveLength, shape.amplitude);
+            ctx.bezierCurveTo(pos[0][0] + left, -pos[0][1] + shape.waterLevel,
+                pos[1][0] + left, -pos[1][1] + shape.waterLevel,
+                pos[2][0] + left, -pos[2][1] + shape.waterLevel);
+
+            if (c === curves - 1) {
+                waveRight = pos[2][0];
+            }
+        }
+
+        if (shape.inverse) {
+            /**
+             * top-right corner
+             *                  2. draws this line
+             *                          |
+             *                       +------+
+             * 3. draws this line -> |      | <- 1. draws this line
+             *                       ~~~~~~~~
+             */
+            ctx.lineTo(waveRight + left, shape.cy - shape.radiusY);
+            ctx.lineTo(left, shape.cy - shape.radiusY);
+            ctx.lineTo(left, shape.waterLevel);
+        }
+        else {
+            /**
+             * top-right corner
+             *
+             *                       ~~~~~~~~
+             * 3. draws this line -> |      | <- 1. draws this line
+             *                       +------+
+             *                          ^
+             *                          |
+             *                  2. draws this line
+             */
+            ctx.lineTo(waveRight + left, shape.cy + shape.radiusY);
+            ctx.lineTo(left, shape.cy + shape.radiusY);
+            ctx.lineTo(left, shape.waterLevel);
+        }
+
+        ctx.closePath();
+    }
+});
+
+
+
+/**
+ * Using Bezier curves to fit sine wave.
+ * There is 4 control points for each curve of wave,
+ * which is at 1/4 wave length of the sine wave.
+ *
+ * The control points for a wave from (a) to (d) are a-b-c-d:
+ *          c *----* d
+ *     b *
+ *       |
+ * ... a * ..................
+ *
+ * whose positions are a: (0, 0), b: (0.5, 0.5), c: (1, 1), d: (PI / 2, 1)
+ *
+ * @param {number} x          x position of the left-most point (a)
+ * @param {number} stage      0-3, stating which part of the wave it is
+ * @param {number} waveLength wave length of the sine wave
+ * @param {number} amplitude  wave amplitude
+ */
+function getWaterPositions(x, stage, waveLength, amplitude) {
+    if (stage === 0) {
+        return [
+            [x + 1 / 2 * waveLength / Math.PI / 2, amplitude / 2],
+            [x + 1 / 2 * waveLength / Math.PI,     amplitude],
+            [x + waveLength / 4,                   amplitude]
+        ];
+    }
+    else if (stage === 1) {
+        return [
+            [x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 2),
+            amplitude],
+            [x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 1),
+            amplitude / 2],
+            [x + waveLength / 4,                   0]
+        ]
+    }
+    else if (stage === 2) {
+        return [
+            [x + 1 / 2 * waveLength / Math.PI / 2, -amplitude / 2],
+            [x + 1 / 2 * waveLength / Math.PI,     -amplitude],
+            [x + waveLength / 4,                   -amplitude]
+        ]
+    }
+    else {
+        return [
+            [x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 2),
+            -amplitude],
+            [x + 1 / 2 * waveLength / Math.PI / 2 * (Math.PI - 1),
+            -amplitude / 2],
+            [x + waveLength / 4,                   0]
+        ]
+    }
+}
+
+
+/***/ }),
+
+/***/ "./src/liquidFillSeries.js":
+/*!*********************************!*\
+  !*** ./src/liquidFillSeries.js ***!
+  \*********************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var completeDimensions = __webpack_require__(/*! echarts/lib/data/helper/completeDimensions */ "./node_modules/echarts/lib/data/helper/completeDimensions.js");
+var echarts = __webpack_require__(/*! echarts/lib/echarts */ "echarts/lib/echarts");
+
+echarts.extendSeriesModel({
+
+    type: 'series.liquidFill',
+
+    visualColorAccessPath: 'textStyle.normal.color',
+
+    optionUpdated: function () {
+        var option = this.option;
+        option.gridSize = Math.max(Math.floor(option.gridSize), 4);
+    },
+
+    getInitialData: function (option, ecModel) {
+        var dimensions = completeDimensions(['value'], option.data);
+        var list = new echarts.List(dimensions, this);
+        list.initData(option.data);
+        return list;
+    },
+
+    defaultOption: {
+        color: ['#294D99', '#156ACF', '#1598ED', '#45BDFF'],
+        center: ['50%', '50%'],
+        radius: '50%',
+        amplitude: '8%',
+        waveLength: '80%',
+        phase: 'auto',
+        period: 'auto',
+        direction: 'right',
+        shape: 'circle',
+
+        waveAnimation: true,
+        animationEasing: 'linear',
+        animationEasingUpdate: 'linear',
+        animationDuration: 2000,
+        animationDurationUpdate: 1000,
+
+        outline: {
+            show: true,
+            borderDistance: 8,
+            itemStyle: {
+                color: 'none',
+                borderColor: '#294D99',
+                borderWidth: 8,
+                shadowBlur: 20,
+                shadowColor: 'rgba(0, 0, 0, 0.25)'
+            }
+        },
+
+        backgroundStyle: {
+            color: '#E3F7FF'
+        },
+
+        itemStyle: {
+            opacity: 0.95,
+            shadowBlur: 50,
+            shadowColor: 'rgba(0, 0, 0, 0.4)'
+        },
+
+        label: {
+            show: true,
+            color: '#294D99',
+            insideColor: '#fff',
+            fontSize: 50,
+            fontWeight: 'bold',
+
+            align: 'center',
+            baseline: 'middle',
+            position: 'inside'
+        },
+
+        emphasis: {
+            itemStyle: {
+                opacity: 0.8
+            }
+        }
+    }
+});
+
+
+/***/ }),
+
+/***/ "./src/liquidFillView.js":
+/*!*******************************!*\
+  !*** ./src/liquidFillView.js ***!
+  \*******************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+var echarts = __webpack_require__(/*! echarts/lib/echarts */ "echarts/lib/echarts");
+var numberUtil = echarts.number;
+var symbolUtil = __webpack_require__(/*! echarts/lib/util/symbol */ "./node_modules/echarts/lib/util/symbol.js");
+var parsePercent = numberUtil.parsePercent;
+
+var LiquidLayout = __webpack_require__(/*! ./liquidFillLayout */ "./src/liquidFillLayout.js");
+
+function getShallow(model, path) {
+    return model && model.getShallow(path);
+}
+
+echarts.extendChartView({
+
+    type: 'liquidFill',
+
+    render: function (seriesModel, ecModel, api) {
+        var group = this.group;
+        group.removeAll();
+
+        var data = seriesModel.getData();
+
+        var itemModel = data.getItemModel(0);
+
+        var center = itemModel.get('center');
+        var radius = itemModel.get('radius');
+
+        var width = api.getWidth();
+        var height = api.getHeight();
+        var size = Math.min(width, height);
+        // itemStyle
+        var outlineDistance = 0;
+        var outlineBorderWidth = 0;
+        var showOutline = seriesModel.get('outline.show');
+
+        if (showOutline) {
+            outlineDistance = seriesModel.get('outline.borderDistance');
+            outlineBorderWidth = parsePercent(
+                seriesModel.get('outline.itemStyle.borderWidth'), size
+            );
+        }
+
+        var cx = parsePercent(center[0], width);
+        var cy = parsePercent(center[1], height);
+
+        var outterRadius;
+        var innerRadius;
+        var paddingRadius;
+
+        var isFillContainer = false;
+
+        var symbol = seriesModel.get('shape');
+        if (symbol === 'container') {
+            // a shape that fully fills the container
+            isFillContainer = true;
+
+            outterRadius = [
+                width / 2,
+                height / 2
+            ];
+            innerRadius = [
+                outterRadius[0] - outlineBorderWidth / 2,
+                outterRadius[1] - outlineBorderWidth / 2
+            ];
+            paddingRadius = [
+                parsePercent(outlineDistance, width),
+                parsePercent(outlineDistance, height)
+            ];
+
+            radius = [
+                Math.max(innerRadius[0] - paddingRadius[0], 0),
+                Math.max(innerRadius[1] - paddingRadius[1], 0)
+            ];
+        }
+        else {
+            outterRadius = parsePercent(radius, size) / 2;
+            innerRadius = outterRadius - outlineBorderWidth / 2;
+            paddingRadius = parsePercent(outlineDistance, size);
+
+            radius = Math.max(innerRadius - paddingRadius, 0);
+        }
+
+        if (showOutline) {
+            var outline = getOutline();
+            outline.style.lineWidth = outlineBorderWidth;
+            group.add(getOutline());
+        }
+
+        var left = isFillContainer ? 0 : cx - radius;
+        var top = isFillContainer ? 0 : cy - radius;
+
+        var wavePath = null;
+
+        group.add(getBackground());
+
+        // each data item for a wave
+        var oldData = this._data;
+        var waves = [];
+        data.diff(oldData)
+            .add(function (idx) {
+                var wave = getWave(idx, false);
+
+                var waterLevel = wave.shape.waterLevel;
+                wave.shape.waterLevel = isFillContainer ? height / 2 : radius;
+                echarts.graphic.initProps(wave, {
+                    shape: {
+                        waterLevel: waterLevel
+                    }
+                }, seriesModel);
+
+                wave.z2 = 2;
+                setWaveAnimation(idx, wave, null);
+
+                group.add(wave);
+                data.setItemGraphicEl(idx, wave);
+                waves.push(wave);
+            })
+            .update(function (newIdx, oldIdx) {
+                var waveElement = oldData.getItemGraphicEl(oldIdx);
+
+                // new wave is used to calculate position, but not added
+                var newWave = getWave(newIdx, false, waveElement);
+
+                // changes with animation
+                var shape = {};
+                var shapeAttrs = ['amplitude', 'cx', 'cy', 'phase', 'radius', 'radiusY', 'waterLevel', 'waveLength'];
+                for (var i = 0; i < shapeAttrs.length; ++i) {
+                    var attr = shapeAttrs[i];
+                    if (newWave.shape.hasOwnProperty(attr)) {
+                        shape[attr] = newWave.shape[attr];
+                    }
+                }
+
+                var style = {};
+                var styleAttrs = ['fill', 'opacity', 'shadowBlur', 'shadowColor'];
+                for (var i = 0; i < styleAttrs.length; ++i) {
+                    var attr = styleAttrs[i];
+                    if (newWave.style.hasOwnProperty(attr)) {
+                        style[attr] = newWave.style[attr];
+                    }
+                }
+
+                if (isFillContainer) {
+                    shape.radiusY = height / 2;
+                }
+
+                // changes with animation
+                echarts.graphic.updateProps(waveElement, {
+                    shape: shape
+                }, seriesModel);
+
+                waveElement.useStyle(style);
+
+                // instant changes
+                waveElement.position = newWave.position;
+                waveElement.setClipPath(newWave.clipPath);
+                waveElement.shape.inverse = newWave.inverse;
+
+                setWaveAnimation(newIdx, waveElement, waveElement);
+                group.add(waveElement);
+                data.setItemGraphicEl(newIdx, waveElement);
+                waves.push(waveElement);
+            })
+            .remove(function (idx) {
+                var wave = oldData.getItemGraphicEl(idx);
+                group.remove(wave);
+            })
+            .execute();
+
+        if (itemModel.get('label.show')) {
+            group.add(getText(waves));
+        }
+
+        this._data = data;
+
+        /**
+         * Get path for outline, background and clipping
+         *
+         * @param {number} r outter radius of shape
+         * @param {boolean|undefined} isForClipping if the shape is used
+         *                                          for clipping
+         */
+        function getPath(r, isForClipping) {
+            if (symbol) {
+                // customed symbol path
+                if (symbol.indexOf('path://') === 0) {
+                    var path = echarts.graphic.makePath(symbol.slice(7), {});
+                    var bouding = path.getBoundingRect();
+                    var w = bouding.width;
+                    var h = bouding.height;
+                    if (w > h) {
+                        h = r * 2 / w * h;
+                        w = r * 2;
+                    }
+                    else {
+                        w = r * 2 / h * w;
+                        h = r * 2;
+                    }
+
+                    var left = isForClipping ? 0 : cx - w / 2;
+                    var top = isForClipping ? 0 : cy - h / 2;
+                    path = echarts.graphic.makePath(
+                        symbol.slice(7),
+                        {},
+                        new echarts.graphic.BoundingRect(left, top, w, h)
+                    );
+                    if (isForClipping) {
+                        path.position = [-w / 2, -h / 2];
+                    }
+                    return path;
+                }
+                else if (isFillContainer) {
+                    // fully fill the container
+                    var x = isForClipping ? -r[0] : cx - r[0];
+                    var y = isForClipping ? -r[1] : cy - r[1];
+                    return symbolUtil.createSymbol(
+                        'rect', x, y, r[0] * 2, r[1] * 2
+                    );
+                }
+                else {
+                    var x = isForClipping ? -r : cx - r;
+                    var y = isForClipping ? -r : cy - r;
+                    if (symbol === 'pin') {
+                        y += r;
+                    }
+                    else if (symbol === 'arrow') {
+                        y -= r;
+                    }
+                    return symbolUtil.createSymbol(symbol, x, y, r * 2, r * 2);
+                }
+            }
+
+            return new echarts.graphic.Circle({
+                shape: {
+                    cx: isForClipping ? 0 : cx,
+                    cy: isForClipping ? 0 : cy,
+                    r: r
+                }
+            });
+        }
+        /**
+         * Create outline
+         */
+        function getOutline() {
+            var outlinePath = getPath(outterRadius);
+            outlinePath.style.fill = null;
+
+            outlinePath.setStyle(seriesModel.getModel('outline.itemStyle')
+                .getItemStyle());
+
+            return outlinePath;
+        }
+
+        /**
+         * Create background
+         */
+        function getBackground() {
+            // Seperate stroke and fill, so we can use stroke to cover the alias of clipping.
+            var strokePath = getPath(radius);
+            strokePath.setStyle(seriesModel.getModel('backgroundStyle')
+                .getItemStyle());
+            strokePath.style.fill = null;
+
+            // Stroke is front of wave
+            strokePath.z2 = 5;
+
+            var fillPath = getPath(radius);
+            fillPath.setStyle(seriesModel.getModel('backgroundStyle')
+                .getItemStyle());
+            fillPath.style.stroke = null;
+
+            var group = new echarts.graphic.Group();
+            group.add(strokePath);
+            group.add(fillPath);
+
+            return group;
+        }
+
+        /**
+         * wave shape
+         */
+        function getWave(idx, isInverse, oldWave) {
+            var radiusX = isFillContainer ? radius[0] : radius;
+            var radiusY = isFillContainer ? height / 2 : radius;
+
+            var itemModel = data.getItemModel(idx);
+            var itemStyleModel = itemModel.getModel('itemStyle');
+            var phase = itemModel.get('phase');
+            var amplitude = parsePercent(itemModel.get('amplitude'),
+                radiusY * 2);
+            var waveLength = parsePercent(itemModel.get('waveLength'),
+                radiusX * 2);
+
+            var value = data.get('value', idx);
+            var waterLevel = radiusY - value * radiusY * 2;
+            phase = oldWave ? oldWave.shape.phase
+                : (phase === 'auto' ? idx * Math.PI / 4 : phase);
+            var normalStyle = itemStyleModel.getItemStyle();
+            if (!normalStyle.fill) {
+                var seriesColor = seriesModel.get('color');
+                var id = idx % seriesColor.length;
+                normalStyle.fill = seriesColor[id];
+            }
+
+            var x = radiusX * 2;
+            var wave = new LiquidLayout({
+                shape: {
+                    waveLength: waveLength,
+                    radius: radiusX,
+                    radiusY: radiusY,
+                    cx: x,
+                    cy: 0,
+                    waterLevel: waterLevel,
+                    amplitude: amplitude,
+                    phase: phase,
+                    inverse: isInverse
+                },
+                style: normalStyle,
+                position: [cx, cy]
+            });
+            wave.shape._waterLevel = waterLevel;
+
+            var hoverStyle = itemModel.getModel('emphasis.itemStyle')
+                .getItemStyle();
+            hoverStyle.lineWidth = 0;
+            echarts.graphic.setHoverStyle(wave, hoverStyle);
+
+            // clip out the part outside the circle
+            var clip = getPath(radius, true);
+            // set fill for clipPath, otherwise it will not trigger hover event
+            clip.setStyle({
+                fill: 'white'
+            });
+            wave.setClipPath(clip);
+
+            return wave;
+        }
+
+        function setWaveAnimation(idx, wave, oldWave) {
+            var itemModel = data.getItemModel(idx);
+
+            var maxSpeed = itemModel.get('period');
+            var direction = itemModel.get('direction');
+
+            var value = data.get('value', idx);
+
+            var phase = itemModel.get('phase');
+            phase = oldWave ? oldWave.shape.phase
+                : (phase === 'auto' ? idx * Math.PI / 4 : phase);
+
+            var defaultSpeed = function (maxSpeed) {
+                var cnt = data.count();
+                return cnt === 0 ? maxSpeed : maxSpeed *
+                    (0.2 + (cnt - idx) / cnt * 0.8);
+            };
+            var speed = 0;
+            if (maxSpeed === 'auto') {
+                speed = defaultSpeed(5000);
+            }
+            else {
+                speed = typeof maxSpeed === 'function'
+                    ? maxSpeed(value, idx) : maxSpeed;
+            }
+
+            // phase for moving left/right
+            var phaseOffset = 0;
+            if (direction === 'right' || direction == null) {
+                phaseOffset = Math.PI;
+            }
+            else if (direction === 'left') {
+                phaseOffset = -Math.PI;
+            }
+            else if (direction === 'none') {
+                phaseOffset = 0;
+            }
+            else {
+                console.error('Illegal direction value for liquid fill.');
+            }
+
+            // wave animation of moving left/right
+            if (direction !== 'none' && itemModel.get('waveAnimation')) {
+                wave
+                    .animate('shape', true)
+                    .when(0, {
+                        phase: phase
+                    })
+                    .when(speed / 2, {
+                        phase: phaseOffset + phase
+                    })
+                    .when(speed, {
+                        phase: phaseOffset * 2 + phase
+                    })
+                    .during(function () {
+                        if (wavePath) {
+                            wavePath.dirty(true);
+                        }
+                    })
+                    .start();
+            }
+        }
+
+        /**
+         * text on wave
+         */
+        function getText(waves) {
+            var labelModel = itemModel.getModel('label');
+
+            function formatLabel() {
+                var formatted = seriesModel.getFormattedLabel(0, 'normal');
+                var defaultVal = (data.get('value', 0) * 100);
+                var defaultLabel = data.getName(0) || seriesModel.name;
+                if (!isNaN(defaultVal)) {
+                    defaultLabel = defaultVal.toFixed(0) + '%';
+                }
+                return formatted == null ? defaultLabel : formatted;
+            }
+
+            var textOption = {
+                z2: 10,
+                shape: {
+                    x: left,
+                    y: top,
+                    width: (isFillContainer ? radius[0] : radius) * 2,
+                    height: (isFillContainer ? radius[1] : radius) * 2
+                },
+                style: {
+                    fill: 'transparent',
+                    text: formatLabel(),
+                    textAlign: labelModel.get('align'),
+                    textVerticalAlign: labelModel.get('baseline')
+                },
+                silent: true
+            };
+
+            var outsideTextRect = new echarts.graphic.Rect(textOption);
+            var color = labelModel.get('color');
+            echarts.graphic.setText(outsideTextRect.style, labelModel, color);
+
+            var insideTextRect = new echarts.graphic.Rect(textOption);
+            var insColor = labelModel.get('insideColor');
+            echarts.graphic.setText(insideTextRect.style, labelModel, insColor);
+            insideTextRect.style.textFill = insColor;
+
+            var group = new echarts.graphic.Group();
+            group.add(outsideTextRect);
+            group.add(insideTextRect);
+
+            // clip out waves for insideText
+            var boundingCircle = getPath(radius, true);
+
+            wavePath = new echarts.graphic.CompoundPath({
+                shape: {
+                    paths: waves
+                },
+                position: [cx, cy]
+            });
+
+            wavePath.setClipPath(boundingCircle);
+            insideTextRect.setClipPath(wavePath);
+
+            return group;
+        }
+    },
+
+    dispose: function () {
+        // dispose nothing here
+    }
+});
+
+
+/***/ }),
+
+/***/ "echarts/lib/echarts":
+/*!**************************!*\
+  !*** external "echarts" ***!
+  \**************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_echarts_lib_echarts__;
+
+/***/ })
+
+/******/ });
+});
+//# sourceMappingURL=echarts-liquidfill.js.map

文件差異過大導致無法顯示
+ 21 - 0
ec-canvas/echarts.js


+ 121 - 0
ec-canvas/wx-canvas.js

@@ -0,0 +1,121 @@
+export default class WxCanvas {
+  constructor(ctx, canvasId, isNew, canvasNode) {
+    this.ctx = ctx;
+    this.canvasId = canvasId;
+    this.chart = null;
+    this.isNew = isNew
+    if (isNew) {
+      this.canvasNode = canvasNode;
+    }
+    else {
+      this._initStyle(ctx);
+    }
+
+    // this._initCanvas(zrender, ctx);
+
+    this._initEvent();
+  }
+
+  getContext(contextType) {
+    if (contextType === '2d') {
+      return this.ctx;
+    }
+  }
+
+  // canvasToTempFilePath(opt) {
+  //   if (!opt.canvasId) {
+  //     opt.canvasId = this.canvasId;
+  //   }
+  //   return wx.canvasToTempFilePath(opt, this);
+  // }
+
+  setChart(chart) {
+    this.chart = chart;
+  }
+
+  attachEvent() {
+    // noop
+  }
+
+  detachEvent() {
+    // noop
+  }
+
+  _initCanvas(zrender, ctx) {
+    zrender.util.getContext = function () {
+      return ctx;
+    };
+
+    zrender.util.$override('measureText', function (text, font) {
+      ctx.font = font || '12px sans-serif';
+      return ctx.measureText(text);
+    });
+  }
+
+  _initStyle(ctx) {
+    var styles = ['fillStyle', 'strokeStyle', 'globalAlpha',
+      'textAlign', 'textBaseAlign', 'shadow', 'lineWidth',
+      'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize'];
+
+    styles.forEach(style => {
+      Object.defineProperty(ctx, style, {
+        set: value => {
+          if (style !== 'fillStyle' && style !== 'strokeStyle'
+            || value !== 'none' && value !== null
+          ) {
+            ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
+          }
+        }
+      });
+    });
+
+    ctx.createRadialGradient = () => {
+      return ctx.createCircularGradient(arguments);
+    };
+  }
+
+  _initEvent() {
+    this.event = {};
+    const eventNames = [{
+      wxName: 'touchStart',
+      ecName: 'mousedown'
+    }, {
+      wxName: 'touchMove',
+      ecName: 'mousemove'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'mouseup'
+    }, {
+      wxName: 'touchEnd',
+      ecName: 'click'
+    }];
+
+    eventNames.forEach(name => {
+      this.event[name.wxName] = e => {
+        const touch = e.touches[0];
+        this.chart.getZr().handler.dispatch(name.ecName, {
+          zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
+          zrY: name.wxName === 'tap' ? touch.clientY : touch.y
+        });
+      };
+    });
+  }
+
+  set width(w) {
+    if (this.canvasNode) this.canvasNode.width = w
+  }
+  set height(h) {
+    if (this.canvasNode) this.canvasNode.height = h
+  }
+
+  get width() {
+    if (this.canvasNode)
+      return this.canvasNode.width
+    return 0
+  }
+  get height() {
+    if (this.canvasNode)
+      return this.canvasNode.height
+    return 0
+  }
+}

二進制
images/chargeover_1.png


二進制
images/navigation.png


+ 59 - 29
pages/charging/charging.js

@@ -1,7 +1,54 @@
-// pages/charging/charging.js
+import * as echarts from '../../ec-canvas/echarts';
+import * as lf from '../../ec-canvas/echarts-liquidfill';
+let chart = null;
+function initChart(canvas, width, height, dpr) {
+  chart = echarts.init(canvas, lf, {
+    width: width,
+    height: height,
+    devicePixelRatio: dpr // new
+  });
+  canvas.setChart(chart);
+  var options111 = {
+    series: [{
+      type: 'liquidFill',
+      data: [0.3],
+      color: ['#128cec'],//主体填充颜色
+      center: ['50%', '50%'],//中心位置
+      radius: '72%',//大小
+      outline: {
+        show: false
+      },
+      backgroundStyle: {
+        color: '#cceaff'
+      },
+      label: {
+        show: true,
+        color: '#128cec',
+        insideColor: '#ffffff',
+        fontSize: 20,
+        fontWeight: 'bold',
+        align: 'center',
+        baseline: 'middle',
+        position: 'inside',
+        formatter: function (p) {
+          console.log(p);
+          if (isNaN(p.value)) {
+            return '启动充电中';
+          }
+          return (p.value * 100).toFixed(2) + '%' + '\n' + '当前电量';
+        }
+      }
+    }]
+  };
+  chart.setOption(options111);
+  return chart;
+}
+
+
 let log = require('../../utils/log.js');
 var chargingPng;
 var chargingStatus;
+
 Page({
 
   /**
@@ -16,32 +63,15 @@ Page({
     yczds:'',
     totalMoney:'',
     chargingOrder: null,
-    chargPile: null
+    chargPile: null,
+    ec: {
+      onInit: initChart
+    }
   },
   drawCircle: function(step) {
-    var context = wx.createCanvasContext('canvasProgress');
-    context.setLineWidth(4); // 设置圆环的宽度
-
-    // 设置渐变
-    //var gradient = context.createLinearGradient(200, 100, 100, 200);
-    //gradient.addColorStop("0", "#2661DD");
-    //gradient.addColorStop("0.5", "#40ED94");
-    //gradient.addColorStop("1.0", "#5956CC");
-
-    context.setLineWidth(this.getSize(20));
-    context.setStrokeStyle('#24e383');
-    context.setLineCap('round')
-    context.beginPath();
-
-    // 参数step 为绘制的圆环周长,从0到2为一周 。 -Math.PI / 2 将起始角设在12点钟位置 ,结束角 通过改变 step 的值确定
-    context.arc(this.getSize(100) - 1, this.getSize(100) - 1, this.getSize(90) - 1, -Math.PI / 2, step / (100 / 2) * Math.PI - Math.PI / 2, false);
-    context.stroke();
-    context.draw()
-  },
-  getSize: function(v) {
-    //console.log(wx.getSystemInfoSync().windowWidth);
-    //console.log(v * (wx.getSystemInfoSync().windowWidth / 375));
-    return v * (wx.getSystemInfoSync().windowWidth / 375)
+    var newoption = chart.getOption();
+    newoption.series[0].data[0]+=0.02;
+    chart.setOption(newoption,true);
   },
   stopCharging(e) {
     wx.showLoading({
@@ -172,14 +202,14 @@ Page({
             soc: chargingRate
           } = chargingOrder;
           let orderid_r = chargingOrder.orderid;
-          console.log(chargingOrder);
+          //console.log(chargingOrder);
           log.info('[充电中]', '[获取充电桩]', '[请求]', chargPileId);
           wx.request({
             url: getApp().globalData.postHeadAgreement + '/restapi/wechat/chargPile',
             data: chargPileId,
             method: 'POST',
             success(res1) {
-              console.log(res1.data);
+              //console.log(res1.data);
               if (res1.data) {
                 let {
                   data: chargPile
@@ -191,7 +221,7 @@ Page({
               }
             }
           });
-          console.log(chargingOrder.chargPileStatus);
+          //console.log(chargingOrder.chargPileStatus);
           if (chargingOrder.chargPileStatus == 1) { //启动中,继续轮训
             log.info('[充电中]', '[初次订单状态判定]', '[chargPileStatus状态1启动中,继续轮训]', data)
             that.setData({
@@ -200,7 +230,7 @@ Page({
               orderid: orderid_r
             });
           } else if (chargingOrder.chargPileStatus == 2) { //充电中,继续轮训
-            console.log(chargingOrder.chargPileStatus == 2);
+            //console.log(chargingOrder.chargPileStatus == 2);
             if (chargingOrder.ljcdsj || chargingOrder.ljcdsj == 0) {
               var chargingTimeHour = Math.floor(chargingOrder.ljcdsj / 60);
               if (chargingTimeHour <= 9) {

+ 4 - 3
pages/charging/charging.json

@@ -1,5 +1,6 @@
 {
-  "usingComponents": {},
-  "navigationBarTitleText": "充电中",
-  "navigationBarBackgroundColor": "#0a181a"
+  "usingComponents": {
+    "ec-canvas": "../../ec-canvas/ec-canvas"
+  },
+  "navigationBarTitleText": "派捷充电桩"
 }

+ 24 - 29
pages/charging/charging.wxml

@@ -1,56 +1,51 @@
 <!--pages/charging/charging.wxml-->
 <view class="map_text" hover-class='active'>
   <view class='charging_text_title'>
-    <view class='charging'>
-      <image src='/images/charging2.png'></image>
-    </view>
     <view class='charging_text_title1'>
       <text class='bold'>{{chargPile.address}}</text>
       <text class="h1">{{chargPile.chargPileName}}</text>
     </view>
   </view>
   <text class="left">终端编号:{{chargPileId}}</text>
-  <text class="right">数据每分钟自动刷新</text>
   <view class='chargeover'>
     <image src='/images/chargeover_1.png'></image>
-    <canvas class="progress_canvas" canvas-id="canvasProgress"> </canvas>
+    <!-- <canvas class="progress_canvas" canvas-id="canvasProgress"> </canvas> -->
+    <ec-canvas class="progress_canvas" id="mychart-dom-bar" canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>
     <view class='chargeover_text_view' wx:if="{{chargingOrder.chargPileStatus==2}}">
-      <text class="text2">{{yczds}}</text>
-      <text class="text1">已充电量(度)</text>
+      <!-- <text class="text2">{{yczds}}</text>
+      <text class="text1">已充电量(度)</text> -->
       <!-- <text class="text2">01小时04分</text>
       <text class="text1">剩余充满时间</text> -->
-      <text class="text2">{{chargingRate}}%</text>
-      <text class="text1">电池电量</text>
+      <!-- <text class="text2">{{chargingRate}}%</text> -->
+      <!-- <text class="text1">电池电量</text> -->
     </view>
     <view class='chargeover_text_view' wx:if="{{chargingOrder.chargPileStatus==1}}">
       <text class="text4">充电桩启动中</text>
     </view>
   </view>
 
-
-
-
-
-  <!--<view class='chargeover'>
-    <image src='/images/chargeover_1.png'></image>
-    <image src='/images/chargeover{{chargingRate}}.png'></image>
-    <view class='chargeover_text_view'>
-      <text class="text2">17.15</text>
-      <text class="text1">已充电量(度)</text>-->
-      <!-- <text class="text2">01小时04分</text>
-      <text class="text1">剩余充满时间</text> -->
-      <!--<text class="text2">{{chargingRate}}%</text>
-      <text class="text1">电池电量</text>
-    </view>
-  </view> -->
   <view class='text_context' >
     <text class="text1">已充时长</text>
     <text class="text3">{{chargingTime}}</text>
-    <text class="text1">充电费用</text>
-    <text class="text3 inline">{{totalMoney}}</text>
-    <text class="inline">元</text>
   </view>
-  <text class="help" decode='true'>客服电话:18211188302&nbsp;&nbsp;&nbsp;&nbsp;工作时间:工作日 08:00-17:00</text>
+  <view class="context">
+    <view class='context_view' bindtap="goPurse" hover-class='active'>
+      <image class='chargemoney' src='/images/chargemoney.png'></image>
+      <text>电流</text>
+      <text class='text2'>3.5kW-16A</text>
+    </view>
+    <view class='context_view1' bindtap="goCharginglog" hover-class='active'>
+      <image class='relevance' src='/images/relevance.png'></image>
+      <text>电压</text>
+      <text class='text2'>220V</text>
+    </view>
+    <view class='context_view' hover-class='active'>
+      <image class='exchange' src='/images/exchange.png'></image>
+      <text>费用</text>
+      <text class='text2'>{{totalMoney}}元</text>
+    </view>
+  </view>
+  <!-- <text class="help" decode='true'>客服电话:18211188302&nbsp;&nbsp;&nbsp;&nbsp;工作时间:工作日 08:00-17:00</text> -->
 </view>
 
 <button class='{{chargingOrder.chargPileStatus==2?"charg_button":"charg_button_disable"}}' bindtap='{{chargingOrder.chargPileStatus==2?"stopCharging":""}}'>停止充电</button>

+ 109 - 79
pages/charging/charging.wxss

@@ -1,22 +1,19 @@
 /* pages/charging/charging.wxss */
 
 page {
-  background-color: #0a181a;
+  background-color: #fff;
   height: 100%;
   overflow-y: hidden;
+  
 }
 
 .map_text {
   position: relative;
   height: calc(100% - 210rpx);
-  background: #0a181a;
+  background: #fff;
   padding: 0 30rpx;
-  margin: 30rpx 20rpx 50rpx 20rpx;
-  border-radius: 20rpx;
-}
-
-.map_text.active {
-  /*background-color: #ebebeb;*/
+  margin: 0;
+  box-shadow: 0rpx 6rpx 6rpx  #f5f5f5 inset; 
 }
 
 .charging_text_title {
@@ -32,31 +29,18 @@ page {
   overflow: hidden;
 }
 
-.charging {
-  width: 45rpx;
-  height: 45rpx;
-  margin: 30rpx 30rpx 30rpx 0;
-  flex-shrink: 0;
-  overflow: hidden;
-}
-
-.charging image {
-  width: 45rpx;
-  height: 45rpx;
-}
-
 .chargeover {
-  width: 400rpx;
-  height: 400rpx;
-  margin: 50rpx 125rpx;
+  width: 585rpx;
+  height: 475rpx;
+  margin: 50rpx 55rpx;
   position: relative;
   flex-shrink: 0;
   overflow: hidden;
 }
 
 .chargeover image {
-  width: 400rpx;
-  height: 400rpx;
+  width: 585rpx;
+  height: 474rpx;
   position: absolute;
   top: 0;
   left: 0;
@@ -83,7 +67,7 @@ text {
                  Geneva,
                  Verdana,
                  sans-serif;
-  color: #24e383;
+  color: #434343;
   text-overflow: ellipsis;
   white-space: nowrap;
   overflow: hidden;
@@ -94,12 +78,12 @@ text {
 
 text.bold {
   font-weight: bold;
-  line-height: 24rpx;
-  height: 24rpx;
-  min-height: 24rpx;
-  margin-top: 30rpx;
-  margin-bottom: 1rpx;
+  line-height: 50rpx;
+  height: 50rpx;
+  min-height: 50rpx;
+  margin: 20rpx 0 0 0;
   display: block;
+  font-size: 32rpx;
 }
 
 text.inline {
@@ -112,35 +96,22 @@ text.inline {
 }
 text.left {
   float: left;
-  padding-bottom: 50rpx;
+  padding-top: 40rpx;
   margin: 0;
+  color: #666666;
 }
-text.right {
-  text-align: right;
-  float: right;
-  padding-bottom: 50rpx;
-  margin: 0;
-}
-text.fast {
-  color: #2483c0;
-}
-
-text.slow {
-  color: #42b9b4;
-}
-
 .h1 {
   margin: 0rpx 0rpx 30rpx 0rpx;
-  font-size: 20rpx;
-  line-height: 20rpx;
-  height: 20rpx;
-  min-height: 20rpx;
-  color: #959595;
+  font-size: 22rpx;
+  line-height: 42rpx;
+  height: 42rpx;
+  min-height: 42rpx;
+  color: #9d9d9d;
 }
 
 .charg_button {
   font-size: 32rpx;
-  margin: 50rpx 30rpx;
+  margin: 50rpx 65rpx;
   font-family: 'Lucida Sans',
                  'Lucida Sans Regular',
                  'Lucida Grande',
@@ -148,15 +119,16 @@ text.slow {
                  Geneva,
                  Verdana,
                  sans-serif;
-  color: #1eb368;
+  color: #ffffff;
   text-overflow: ellipsis;
   white-space: nowrap;
-  background-color: #041008;
+  background-color: #1d9bf7;
+  border-radius: 40rpx;
 }
 
 .charg_button.button-hover {
-  background-color: #1eb368;
-  color: #041008;
+  background-color: #1d9cf7b9;
+  color: #ffffff;
 }
 
 .charg_button_disable {
@@ -177,7 +149,8 @@ text.slow {
 
 .text1 {
   margin: 0rpx 0rpx 30rpx 0rpx;
-  color: #959595;
+  color: #03a9dc;
+  font-size: 22rpx;
 }
 
 .text2 {
@@ -189,39 +162,96 @@ text.slow {
 }
 
 .text3 {
-  margin: 0rpx 0rpx 80rpx 0rpx;
-  font-size: 48rpx;
+  margin: 0rpx 0rpx 10rpx 0rpx;
+  font-size: 30rpx;
   line-height: 48rpx;
   height: 48rpx;
   min-height: 48rpx;
-  color: #959595;
-}
-
-.text4 {
-  font-size: 32rpx;
-  line-height: 200rpx;
-  height: 200rpx;
-  min-height: 200rpx;
+  color: #03a9dc;
+  font-weight: bold;
 }
 
 .text_context {
   text-align: center;
 }
 
-.progress_canvas {
-  width: 400rpx;
-  height: 400rpx;
-}
+/* .progress_canvas {
+  width: 350rpx;
+  height: 350rpx;
+} */
 
-.help {
+
+.context{
+  background: #fff;
+  /* margin-top: 60rpx; */
+  height: 178rpx;
   width: 100%;
   text-align: center;
+  min-height: 178rpx;
+  font-family: 'Lucida Sans',
+                 'Lucida Sans Regular',
+                 'Lucida Grande',
+                 'Lucida Sans Unicode',
+                 Geneva,
+                 Verdana,
+                 sans-serif;
+  font-size: 24rpx;
+  color: #434343;
+  /* margin-top:60rpx; */
+  margin: 0;
+}
+.context_view {
+  width: 25%;
+  /* width: 30%; */
+  height: 178rpx;
+  display: inline-block;
+}
+.context_view1 {
+  width: 50%;
+  /* width: 30%; */
+  height: 178rpx;
+  display: inline-block;
+}
+.context_view.active{
+  background-color: #fafafa;
+}
+
+image.chargemoney {
+  margin: 30rpx 0 20rpx 0;
+  width: 52rpx;
+  height: 52rpx;
+  flex-shrink: 0;
+  overflow: hidden;
+}
+image.relevance {
+  margin: 33rpx 0 20rpx 0;
+  width: 49rpx;
+  height: 49rpx;
+  flex-shrink: 0;
+  overflow: hidden;
+}
+image.out {
+  margin: 30rpx 0 20rpx 0;
+  width: 52rpx;
+  height: 52rpx;
+  flex-shrink: 0;
+  overflow: hidden;
+}
+image.exchange {
+  margin: 40rpx 0 20rpx 0;
+  width: 56rpx;
+  height: 42rpx;
+  flex-shrink: 0;
+  overflow: hidden;
+}
+
+.context text {
+  display: block;
+  font-size: 24rpx;
   text-overflow: ellipsis;
   white-space: nowrap;
-  margin: 0rpx 0rpx 30rpx 0rpx;
-  font-size: 22rpx;
-  line-height: 22rpx;
-  height: 22rpx;
-  min-height: 22rpx;
-  color: #959595;
+  overflow: hidden;
+  line-height: 32rpx;
+  height: 24rpx;
+  min-height: 32rpx;
 }

+ 4 - 4
pages/index/index.js

@@ -424,10 +424,10 @@ Page({
     //console.log(e);
     let {
       markerId
-    } = e;
-    //console.log({
-    //  markerId
-    //});
+    } = e.detail;
+    // console.log({
+    //   e
+    // });
     let {
       markers
     } = this.data;

+ 44 - 60
pages/index/index.wxml

@@ -6,86 +6,70 @@
     </view>
     <view class='city_select'>北京</view>
     <view class='down'>
-     <image src='/images/down.png'></image>
+      <image src='/images/down.png'></image>
     </view>
   </view>
-  <view class="{{textData.name?userInfo.flag?'map_container1':'map_container':'map_container_big'}}">
+  <view class="{{textData.name?'map_container':'map_container_big'}}">
+    <view wx:if="{{textData}}" class='map_textdata_fast'>
+      <text decode="true">●&nbsp;快充&nbsp;{{textData.fastfreenum==null?0:textData.fastfreenum}}/</text>
+      <text style='color:#969696;'>{{textData.fastCharg==null?0:textData.fastCharg}}</text>
+    </view>
+    <view wx:if="{{textData}}" class='map_textdata_slow'>
+      <text decode="true">●&nbsp;慢充&nbsp;{{textData.slowfreenum==null?0:textData.slowfreenum}}/</text>
+      <text style='color:#969696;'>{{textData.slowCharg==null?0:textData.slowCharg}}</text>
+    </view>
     <map class="map" id="map" longitude="{{longitude}}" latitude="{{latitude}}" scale="16" show-location="true" markers="{{markers}}" bindmarkertap="makertap" bindregionchange="mapchange" polygon="{{polygon}}">
-      <!-- <view class='map_distance'>
-        <picker class="picker" bindchange="bindDistanceinfoChange" value="{{distanceinfoindex}}" range-key="text" range="{{distanceinfo}}">
-          <text>距离{{distanceinfo[distanceinfoindex].text}}</text>
-        </picker>
-      </view> -->
-      <view wx:if="{{textData}}" class='map_textdata_fast'>
-          <text decode="true">●&nbsp;快充&nbsp;{{textData.fastfreenum==null?0:textData.fastfreenum}}/</text><text style='color:#969696;'>{{textData.fastCharg==null?0:textData.fastCharg}}</text>
-      </view>
-      <view wx:if="{{textData}}" class='map_textdata_slow'>
-          <text decode="true">●&nbsp;慢充&nbsp;{{textData.slowfreenum==null?0:textData.slowfreenum}}/</text><text  style='color:#969696;'>{{textData.slowCharg==null?0:textData.slowCharg}}</text>
-      </view>
       <cover-view class='map_location' bindtap='click_location_control' hover-class='map_location_hover'>
-        <!-- <cover-image class="circle1_1" src='/images/circle1_1.png'></cover-image> -->
         <cover-image class='circle1' src='/images/circle1.png'></cover-image>
       </cover-view>
-      <!-- <cover-view class='map_scan' wx:if="{{!userInfo.flag}}" bindtap='click_scan_control'  hover-class='map_scan_hover'>
-        <cover-image src='/images/scan.png' ></cover-image>
-      </cover-view> -->
     </map>
   </view>
-
-  <view class="{{userInfo.flag?'map_text1':'map_text'}}" hover-class='active' wx:if="{{textData}}">
-    <!-- <view class="map-1" bindtap="getRoute">
-      <image src="/images/jt.png"></image>
-      <view>路线</view>
-    </view> -->
-    <view class='charging_text_title'>
-      <view class='charging'>
-        <image src='/images/charging1.png'></image>
+  <view class="map_text" hover-class='active' wx:if="{{textData}}">
+    <view class="map_text_info">
+      <view class='charging_text_title'>
+        <view class='charging_text_title1'>
+          <text class='bold'>{{textData.name}}</text>
+          <text class="h1">{{textData.address}}</text>
+        </view>
       </view>
-      <view class='charging_text_title1'>
-        <text class='bold'>{{textData.name}}</text>
-        <text class="h1">{{textData.address}}</text>
+      <view class='chargprice'>
+        <text class='inline yellow'>{{textData.chargprice}}</text>
+        <text decode="true" class='inline price'>&nbsp;元/度</text>
       </view>
-      <text class="h1 right">{{textData.distance}}km</text>
-    </view>
-    <view style='line-height:0;'>
-      <text decode='true' class='inline' wx:if="{{userInfo.flag}}">电桩数:{{textData.chargPileNum}}</text>
-      <text decode='true' class='inline' wx:if="{{!userInfo.flag}}">电桩数:{{textData.chargPileNum}}&nbsp;&nbsp;&nbsp;&nbsp;空闲数:</text>
-      <text class='fast inline' wx:if="{{!userInfo.flag}}" decode='true'>快{{textData.fastfreenum==null?0:textData.fastfreenum}}&nbsp;&nbsp;</text>
-      <text class='slow inline' wx:if="{{!userInfo.flag}}">慢{{textData.slowfreenum==null?0:textData.slowfreenum}}</text>
-       <!-- 初始,待运营,运营中,暂停营业,关闭,未运营 -->
-      <text  decode='true'  class='inline' >&nbsp;&nbsp;&nbsp;&nbsp;状态:</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==1}}">初始</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==2}}">待运营</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==3}}">运营中</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==4}}">暂停营业</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==5}}">关闭</text>
-      <text  decode='true'  class='inline' wx:if="{{textData.operationState==6}}">未运营</text>
+      <text class='inline border'>服务费:{{textData.serviceprice}}元/度</text>
+      <text class='inline border'>停车费:{{textData.stopprice}}元/小时</text>
     </view>
-    <text class='fast'>快桩数:{{textData.fastCharg==null?0:textData.fastCharg}}</text>
-    <text class='slow'>慢桩数:{{textData.slowCharg==null?0:textData.slowCharg}}</text>
-    <text class='inline' wx:if="{{userInfo.flag}}">当前故障桩:</text><text class='red inline' wx:if="{{userInfo.flag}}">{{textData.breaknum==null?0:textData.breaknum}}</text>
-    <text  wx:if="{{!userInfo.flag}}">实时参考电费:{{textData.chargprice}}元/度</text>
-    <!--<text decode='true'  wx:if="{{!userInfo.flag}}">实时参考服务费:{{textData.serviceprice}}元/度&nbsp;&nbsp;&nbsp;&nbsp;停车费:{{textData.stopprice}}元/小时</text>-->
-    <text decode='true'  wx:if="{{!userInfo.flag}}">实时参考服务费:{{textData.serviceprice}}元/度</text>
-    <view class='navigation' bindtap="getRoute">
-      <image src='/images/navigation.png'></image>
+    <view class='navigation_info'>
+      <view class='navigation' bindtap="getRoute">
+        <image src='/images/navigation.png'></image>
+      </view>
+      <text>{{textData.distance}}km</text>
     </view>
   </view>
 </view>
-
 <view wx:if='{{!(textData.name)&&showModal&&notice.noticeTitle}}'>
-  <view class="modal_box"> 
-    <view class="title"><image src='/images/notice.png' />公告</view>
+  <view class="modal_box">
+    <view class="title">
+      <image src='/images/notice.png' />公告</view>
     <view class='content_title'>{{notice.noticeTitle}}:</view>
     <view class='content'>
-      <view class='modalMsg' ><rich-text nodes='{{notice.noticeContent}}'></rich-text></view>
+      <view class='modalMsg'>
+        <rich-text nodes='{{notice.noticeContent}}'></rich-text>
+      </view>
     </view>
     <view bindtap='Sure' class='Sure' hover-class='hover_sure'>确定</view>
   </view>
-
 </view>
 <view class='footer'>
-  <view class='footer_1' ><image src='/images/index.png'></image><text class='selected'>首页</text></view>
-  <view class='footer_2' bindtap='click_scan_control'><image src='/images/scan.png'></image></view>
-  <view class='footer_3' bindtap='bindUserAvatarUrl'><image src='/images/ucenter.png'></image><text>我的</text></view>
+  <view class='footer_1'>
+    <image src='/images/index.png'></image>
+    <text class='selected'>首页</text>
+  </view>
+  <view class='footer_2' bindtap='click_scan_control'>
+    <image src='/images/scan.png'></image>
+  </view>
+  <view class='footer_3' bindtap='bindUserAvatarUrl'>
+    <image src='/images/ucenter.png'></image>
+    <text>我的</text>
+  </view>
 </view>

+ 64 - 128
pages/index/index.wxss

@@ -3,7 +3,6 @@
   width: 100%;
   display: flex;
 }
-
 .section .input {
   width: 100%;
   /* margin: 24rpx auto; */
@@ -67,14 +66,6 @@
   left: 0;
   right: 0;
 }
-.map_container1 {
-  position: absolute;
-  top: 89rpx;
-  bottom: 290rpx;
-  /* bottom: 385rpx; */
-  left: 0;
-  right: 0;
-}
 
 .map_container_big {
   position: absolute;
@@ -107,38 +98,17 @@
   display:block;
   margin: 24rpx;
 }
-.map_location .circle1_1 {
-  width: 86rpx;
-  height: 86rpx;
-  display:block;
-}
 
 .map_location_hover{
   transform: scale(0.8, 0.8);
 }
-.map_scan {
-  position: absolute;
-  width: 210rpx;
-  height: 80rpx;
-  bottom: 110rpx;
-  left: 270rpx;
-   z-index:999;
-}
-/*
-.map_scan_hover{
-  margin: 8rpx 21rpx;
-  width: 168rpx;
-  height: 64rpx;
-}*/
+
 
 .map_textdata_fast{
   background-color: #ffffff;
   position: absolute;
-  /* left: 30rpx;
-  bottom: 110rpx; */
   right: 32rpx;
   top: 30rpx;
-  /* width: 86rpx; */
   height: 40rpx;
   z-index:999;
   border-radius: 12rpx;
@@ -149,11 +119,8 @@
 .map_textdata_slow{
   background-color: #ffffff;
   position: absolute;
-  /* left: 30rpx;
-  bottom: 110rpx; */
   right: 32rpx;
   top: 90rpx;
-  /* width: 86rpx; */
   height: 40rpx;
   z-index:999;
   border-radius: 12rpx;
@@ -182,58 +149,56 @@
   bottom: 95rpx;
   height: 230rpx;
   background: #fff;
-  padding: 0 30rpx;
-}
-.map_text1 {
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: 95rpx;
-  height: 195rpx;
-  background: #fff;
-  padding: 0 30rpx;
+  padding: 0 0 0 38rpx;
 }
 .map_text.active,map_text1.active{
   background-color: #ebebeb;
 }
-
-.charging_text_title {
-  height: 105rpx;
-  width: 100%;
-  display: flex;
+.map_text_info{
+  width: 572rpx;
+  display: inline-block;
 }
+
 .charging_text_title1 {
   width: 100%;
   text-overflow: ellipsis;
   white-space: nowrap;
   overflow: hidden;
 }
-
-.charging {
-  width: 45rpx;
-  height: 45rpx;
-  margin: 30rpx 30rpx 30rpx 0;
-  flex-shrink: 0;
-  overflow: hidden;
+.chargprice {
+  height: 50rpx;
+  margin-bottom: 4rpx;
 }
-
-.charging image {
-  width: 45rpx;
-  height: 45rpx;
+.chargprice text {
+  height: 50rpx;
+  min-height: 50rpx;
+  line-height: 50rpx;
+}
+.navigation_info{
+  width: 140rpx;
+  height: 230rpx;
+  margin:0;
+  padding-top: 70rpx;
+  float: right;
 }
-
 .navigation {
-  position: absolute;
-  right: 0;
-  bottom: 0;
-  margin: 30rpx;
-  width: 64rpx;
-  height: 64rpx;
+  width: 80rpx;
+  height: 80rpx;
+  background-color: #21adff;
+  border-radius: 40rpx;
 }
 
 .navigation image {
-  width: 64rpx;
-  height: 64rpx;
+  width: 34rpx;
+  height: 40rpx;
+  margin: 20rpx 20rpx 20rpx 26rpx;
+}
+.navigation_info text {
+  font-size: 22rpx;
+  line-height: 42rpx;
+  height: 42rpx;
+  min-height: 42rpx;
+  margin: 0;
 }
 
 text {
@@ -258,76 +223,47 @@ text {
 
 text.bold {
   font-weight: bold;
-  line-height: 24rpx;
-  height: 24rpx;
-  min-height: 24rpx;
-  margin-top: 30rpx;
-  margin-bottom: 1rpx;
+  line-height: 50rpx;
+  height: 50rpx;
+  min-height: 50rpx;
+  margin: 20rpx 0 0 0;
   display: block;
+  font-size: 32rpx;
 }
 text.inline{
  display: inline-block;
+ margin:0;
 }
-text.fast {
-  color: #2483c0;
-}
-text.slow {
-  color: #42b9b4;
-}
-text.red {
-  color: red;
-}
-
 .h1 {
-  margin: 0rpx 0rpx 30rpx 0rpx;
-  font-size: 20rpx;
-  line-height: 20rpx;
-  height: 20rpx;
-  min-height: 20rpx;
-  color: #959595;
+  font-size: 22rpx;
+  line-height: 42rpx;
+  height: 42rpx;
+  min-height: 42rpx;
+  color: #9d9d9d;
+  margin: 0;
 }
-.h1.right {
-  margin:55rpx 0rpx 30rpx 30rpx;
-  flex-shrink: 0;
-  float: right;
+text.border{
+ border: 2rpx #d8d8d8 solid;
+ border-radius: 20rpx;
+ line-height: 40rpx;
+ height: 40rpx;
+ min-height: 40rpx;
+ padding:0 20rpx;
+ color: #9d9d9d;
+ font-size: 20rpx;
+ margin-right: 15rpx;
+}
+text.yellow {
+  color: #da8527;
+  font-size: 26rpx;
+  /* font-weight: bold; */
 }
-
-/*
-.map-1 {
-  width: 60px;
-  height: 60px;
-  border-radius: 30px;
-  background-color: #4d8ad7;
-  display: flex;
-  flex-direction: column;
-  justify-content: center;
-  align-items: center;
-  color: #fff;
-  font-size: 15px;
-  position: absolute;
-  top: 10px;
-  right: 10px;
+text.price {
+  font-size: 20rpx;
+  color: #9d9d9d;
 }
 
-.map-1 image {
-  width: 24px;
-  height: 32px;
-}
-*/
 
-/* .picker{
-  text-align: right;
-  position: absolute;
-  left: 30rpx;
-  top: 30rpx;  
-}
-.picker text{
-  color: #545253;
-  margin: 0;
-  padding: 4rpx 10rpx 4rpx 10rpx;
-  border:solid 2rpx #545253;
-  border-radius: 8rpx;
-} */
 
 /* 通知相关 begin */
 .modal_box {

+ 1 - 1
pages/search_result/search_result.wxss

@@ -27,7 +27,7 @@ page {
 }
 
 .header_left, .header_right {
-  width: 50%;
+  width: calc(50% - 1rpx);
   display: inline-block;
   background-color: #fff;
   line-height: 48rpx;

+ 66 - 0
pages/ucenter/help/help.js

@@ -0,0 +1,66 @@
+// pages/ucenter/help/help.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad: function (options) {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide: function () {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload: function () {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh: function () {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom: function () {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage: function () {
+
+  }
+})

+ 3 - 0
pages/ucenter/help/help.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 2 - 0
pages/ucenter/help/help.wxml

@@ -0,0 +1,2 @@
+<!--pages/ucenter/help/help.wxml-->
+<text>pages/ucenter/help/help.wxml</text>

+ 1 - 0
pages/ucenter/help/help.wxss

@@ -0,0 +1 @@
+/* pages/ucenter/help/help.wxss */

+ 7 - 1
project.config.json

@@ -9,7 +9,7 @@
 		"autoAudits": false
 	},
 	"compileType": "miniprogram",
-	"libVersion": "2.13.1",
+	"libVersion": "2.14.1",
 	"appid": "wx6e03e5ada030b1aa",
 	"projectname": "充电桩",
 	"isGameTourist": false,
@@ -87,6 +87,12 @@
 					"pathName": "pages/chargemoney/chargemoney",
 					"query": "",
 					"scene": null
+				},
+				{
+					"id": -1,
+					"name": "充电中",
+					"pathName": "pages/charging/charging",
+					"scene": null
 				}
 			]
 		}

部分文件因文件數量過多而無法顯示