UNPKG

echarts

Version:

A powerful charting and visualization library for browser

1,922 lines (1,702 loc) 1.85 MB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.echarts = {}))); }(this, (function (exports) { 'use strict'; /* * 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; /** * zrender: 生成唯一id * * @author errorrik (errorrik@gmail.com) */ var idStart = 0x0907; var guid = function () { return idStart++; }; /** * echarts设备环境识别 * * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。 * @author firede[firede@firede.us] * @desc thanks zepto. */ 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 env$1 = 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: 'onpointerdown' in window // Firefox supports pointer but not by default, only MS browsers are reliable on pointer // events currently. So we dont use that on other browsers unless tested sufficiently. // Although IE 10 supports pointer event, it use old style and is different from the // standard. So we exclude that. (IE 10 is hardly used on touch device) && (browser.edge || (browser.ie && browser.version >= 11)), // 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 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$1(source) || !isObject$1(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$1(sourceProp) && isObject$1(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) { 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$1(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$1(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$1(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) { 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$1(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$1(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)); for (var key in this.data) { this.data.hasOwnProperty(key) && cb(this.data[key], key); } }, // 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() {} var zrUtil = (Object.freeze || Object)({ $override: $override, clone: clone, merge: merge, mergeAll: mergeAll, extend: extend, defaults: defaults, createCanvas: createCanvas, getContext: getContext, indexOf: indexOf, inherits: inherits, mixin: mixin, isArrayLike: isArrayLike, each: each$1, map: map, reduce: reduce, filter: filter, find: find, bind: bind, curry: curry, isArray: isArray, isFunction: isFunction$1, isString: isString, isObject: isObject$1, isBuiltInObject: isBuiltInObject, isTypedArray: isTypedArray, isDom: isDom, eqNaN: eqNaN, retrieve: retrieve, retrieve2: retrieve2, retrieve3: retrieve3, slice: slice, normalizeCssArray: normalizeCssArray, assert: assert$1, trim: trim, setAsPrimitive: setAsPrimitive, isPrimitive: isPrimitive, createHashMap: createHashMap, concatArray: concatArray, noop: noop }); 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$1(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; } var vector = (Object.freeze || Object)({ create: create, copy: copy, clone: clone$1, set: set, add: add, scaleAndAdd: scaleAndAdd, sub: sub, len: len, length: length, lenSquare: lenSquare, lengthSquare: lengthSquare, mul: mul, div: div, dot: dot, scale: scale, normalize: normalize, distance: distance, dist: dist, distanceSquare: distanceSquare, distSquare: distSquare, negate: negate, lerp: lerp, applyTransform: applyTransform, min: min, max: max }); // TODO Draggable for group // FIXME Draggable on element which has parent rotation or scale function Draggable() { this.on('mousedown', this._dragStart, this); this.on('mousemove', this._drag, this); this.on('mouseup', this._dragEnd, this); this.on('globalout', this._dragEnd, this); // this._dropTarget = null; // this._draggingTarget = null; // this._x = 0; // this._y = 0; } Draggable.prototype = { constructor: Draggable, _dragStart: function (e) { var draggingTarget = e.target; if (draggingTarget && draggingTarget.draggable) { this._draggingTarget = draggingTarget; draggingTarget.dragging = true; this._x = e.offsetX; this._y = e.offsetY; this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event); } }, _drag: function (e) { var draggingTarget = this._draggingTarget; if (draggingTarget) { var x = e.offsetX; var y = e.offsetY; var dx = x - this._x; var dy = y - this._y; this._x = x; this._y = y; draggingTarget.drift(dx, dy, e); this.dispatchToElement(param(draggingTarget, e), 'drag', e.event); var dropTarget = this.findHover(x, y, draggingTarget).target; var lastDropTarget = this._dropTarget; this._dropTarget = dropTarget; if (draggingTarget !== dropTarget) { if (lastDropTarget && dropTarget !== lastDropTarget) { this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event); } if (dropTarget && dropTarget !== lastDropTarget) { this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event); } } } }, _dragEnd: function (e) { var draggingTarget = this._draggingTarget; if (draggingTarget) { draggingTarget.dragging = false; } this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event); if (this._dropTarget) { this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event); } this._draggingTarget = null; this._dropTarget = null; } }; function param(target, e) { return {target: target, topTarget: e && e.topTarget}; } /** * 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] Call 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. * @param {Function} [handler] The event handler. */ off: function (event, handler) { var _h = this._$handlers; if (!event) { this._$handlers = {}; return this; } if (handler) { if (_h[event]) { var newList = []; for (var i = 0, l = _h[event].length; i < l; i++) { if (_h[event][i].h !== handler) { newList.push(_h[event][i]); } } _h[event] = newList; } if (_h[event] && _h[event].length === 0) { delete _h[event]; } } else { delete _h[event]; } return this; }, /** * 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; } /** * 事件辅助类 * @module zrender/core/event * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) */ var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; function getBoundingClientRect(el) { // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0}; } // `calculate` is optional, default false function clientToLocal(el, e, out, calculate) { out = out || {}; // According to the W3C Working Draft, offsetX and offsetY should be relative // to the padding edge of the target element. The only browser using this convention // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does // not support the properties. // (see http://www.jacklmoore.com/notes/mouse-position/) // In zr painter.dom, padding edge equals to border edge. // FIXME // When mousemove event triggered on ec tooltip, target is not zr painter.dom, and // offsetX/Y is relative to e.target, where the calculation of zrX/Y via offsetX/Y // is too complex. So css-transfrom dont support in this case temporarily. if (calculate || !env$1.canvasSupported) { defaultGetZrXY(el, e, out); } // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned // ancestor element, so we should make sure el is positioned (e.g., not position:static). // BTW1, Webkit don't return the same results as FF in non-simple cases (like add // zoom-factor, overflow / opacity layers, transforms ...) // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d. // <https://bugs.jquery.com/ticket/8523#comment:14> // BTW3, In ff, offsetX/offsetY is always 0. else if (env$1.browser.firefox && e.layerX != null && e.layerX !== e.offsetX) { out.zrX = e.layerX; out.zrY = e.layerY; } // For IE6+, chrome, safari, opera. (When will ff support offsetX?) else if (e.offsetX != null) { out.zrX = e.offsetX; out.zrY = e.offsetY; } // For some other device, e.g., IOS safari. else { defaultGetZrXY(el, e, out); } return out; } function defaultGetZrXY(el, e, out) { // This well-known method below does not support css transform. var box = getBoundingClientRect(el); out.zrX = e.clientX - box.left; out.zrY = e.clientY - box.top; } /** * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标. * `calculate` is optional, default false. */ function normalizeEvent(el, e, calculate) { e = e || window.event; if (e.zrX != null) { return e; } var eventType = e.type; var isTouch = eventType && eventType.indexOf('touch') >= 0; if (!isTouch) { clientToLocal(el, e, e, calculate); e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3; } else { var touch = eventType !== 'touchend' ? e.targetTouches[0] : e.changedTouches[0]; touch && clientToLocal(el, touch, e, calculate); } // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0; // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js // If e.which has been defined, if may be readonly, // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which var button = e.button; if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) { e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); } // [Caution]: `e.which` from browser is not always reliable. For example, // when press left button and `mousemove (pointermove)` in Edge, the `e.which` // is 65536 and the `e.button` is -1. But the `mouseup (pointerup)` and // `mousedown (pointerdown)` is the same as Chrome does. return e; } /** * @param {HTMLElement} el * @param {string} name * @param {Function} handler */ function addEventListener(el, name, handler) { if (isDomLevel2) { // Reproduct the console warning: // [Violation] Added non-passive event listener to a scroll-blocking <some> event. // Consider marking event handler as 'passive' to make the page more responsive. // Just set console log level: verbose in chrome dev tool. // then the warning log will be printed when addEventListener called. // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md // We have not yet found a neat way to using passive. Because in zrender the dom event // listener delegate all of the upper events of element. Some of those events need // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts. // Before passive can be adopted, these issues should be considered: // (1) Whether and how a zrender user specifies an event listener passive. And by default, // passive or not. // (2) How to tread that some zrender event listener is passive, and some is not. If // we use other way but not preventDefault of mousewheel and touchmove, browser // compatibility should be handled. // var opts = (env.passiveSupported && name === 'mousewheel') // ? {passive: true} // // By default, the third param of el.addEventListener is `capture: false`. // : void 0; // el.addEventListener(name, handler /* , opts */); el.addEventListener(name, handler); } else { el.attachEvent('on' + name, handler); } } function removeEventListener(el, name, handler) { if (isDomLevel2) { el.removeEventListener(name, handler); } else { el.detachEvent('on' + name, handler); } } /** * preventDefault and stopPropagation. * Notice: do not do that in zrender. Upper application * do that if necessary. * * @memberOf module:zrender/core/event * @method * @param {Event} e : event对象 */ var stop = isDomLevel2 ? function (e) { e.preventDefault(); e.stopPropagation(); e.cancelBubble = true; } : function (e) { e.returnValue = false; e.cancelBubble = true; }; /** * This method only works for mouseup and mousedown. The functionality is restricted * for fault tolerance, See the `e.which` compatibility above. * * @param {MouseEvent} e * @return {boolean} */ function isMiddleOrRightButtonOnMouseUpDown(e) { return e.which === 2 || e.which === 3; } /** * To be removed. * @deprecated */ /** * Only implements needed gestures for mobile. */ var GestureMgr = function () { /** * @private * @type {Array.<Object>} */ this._track = []; }; GestureMgr.prototype = { constructor: GestureMgr, recognize: function (event, target, root) { this._doTrack(event, target, root); return this._recognize(event); }, clear: function () { this._track.length = 0; return this; }, _doTrack: function (event, target, root) { var touches = event.touches; if (!touches) { return; } var trackItem = { points: [], touches: [], target: target, event: event }; for (var i = 0, len = touches.length; i < len; i++) { var touch = touches[i]; var pos = clientToLocal(root, touch, {}); trackItem.points.push([pos.zrX, pos.zrY]); trackItem.touches.push(touch); } this._track.push(trackItem); }, _recognize: function (event) { for (var eventName in recognizers) { if (recognizers.hasOwnProperty(eventName)) { var gestureInfo = recognizers[eventName](this._track, event); if (gestureInfo) { return gestureInfo; } } } } }; function dist$1(pointPair) { var dx = pointPair[1][0] - pointPair[0][0]; var dy = pointPair[1][1] - pointPair[0][1]; return Math.sqrt(dx * dx + dy * dy); } function center(pointPair) { return [ (pointPair[0][0] + pointPair[1][0]) / 2, (pointPair[0][1] + pointPair[1][1]) / 2 ]; } var recognizers = { pinch: function (track, event) { var trackLen = track.length; if (!trackLen) { return; } var pinchEnd = (track[trackLen - 1] || {}).points; var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd; if (pinchPre && pinchPre.length > 1 && pinchEnd && pinchEnd.length > 1 ) { var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre); !isFinite(pinchScale) && (pinchScale = 1); event.pinchScale = pinchScale; var pinchCenter = center(pinchEnd); event.pinchX = pinchCenter[0]; event.pinchY = pinchCenter[1]; return { type: 'pinch', target: track[0].target, event: event }; } } // Only pinch currently. }; var SILENT = 'silent'; function makeEventPacket(eveType, targetInfo, event) { return { type: eveT