package.dist.zrender.js.map Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zrender Show documentation
Show all versions of zrender Show documentation
A lightweight graphic library providing 2d draw for Apache ECharts
The newest version!
{"version":3,"file":"zrender.js","sources":["../src/core/env.ts","../src/core/platform.ts","../src/core/util.ts","../src/core/vector.ts","../src/mixin/Draggable.ts","../src/core/Eventful.ts","../src/core/fourPointsTransform.ts","../src/core/dom.ts","../src/core/event.ts","../src/core/GestureMgr.ts","../src/core/matrix.ts","../src/core/Point.ts","../src/core/BoundingRect.ts","../src/Handler.ts","../src/core/timsort.ts","../src/graphic/constants.ts","../src/Storage.ts","../src/animation/requestAnimationFrame.ts","../src/animation/easing.ts","../src/core/curve.ts","../src/animation/cubicEasing.ts","../src/animation/Clip.ts","../src/core/LRU.ts","../src/tool/color.ts","../src/svg/helper.ts","../src/animation/Animator.ts","../src/animation/Animation.ts","../src/dom/HandlerProxy.ts","../src/config.ts","../src/core/Transformable.ts","../src/contain/text.ts","../src/Element.ts","../src/graphic/Group.ts","../src/zrender.ts","../src/graphic/Displayable.ts","../src/core/bbox.ts","../src/core/PathProxy.ts","../src/contain/line.ts","../src/contain/cubic.ts","../src/contain/quadratic.ts","../src/contain/util.ts","../src/contain/arc.ts","../src/contain/windingLine.ts","../src/contain/path.ts","../src/graphic/Path.ts","../src/tool/transformPath.ts","../src/tool/path.ts","../src/graphic/Image.ts","../src/graphic/shape/Circle.ts","../src/graphic/helper/roundRect.ts","../src/graphic/helper/subPixelOptimize.ts","../src/graphic/shape/Rect.ts","../src/graphic/shape/Ellipse.ts","../src/graphic/shape/Line.ts","../src/graphic/helper/smoothBezier.ts","../src/graphic/helper/poly.ts","../src/graphic/shape/Polygon.ts","../src/graphic/shape/Polyline.ts","../src/graphic/Gradient.ts","../src/graphic/LinearGradient.ts","../src/graphic/RadialGradient.ts","../src/graphic/TSpan.ts","../src/tool/parseXML.ts","../src/tool/parseSVG.ts","../src/graphic/helper/roundSector.ts","../src/graphic/shape/Sector.ts","../src/tool/convertPath.ts","../src/tool/dividePath.ts","../src/tool/morphPath.ts","../src/graphic/CompoundPath.ts","../src/graphic/IncrementalDisplayable.ts","../src/graphic/helper/image.ts","../src/graphic/helper/parseText.ts","../src/graphic/Text.ts","../src/graphic/shape/Arc.ts","../src/graphic/shape/BezierCurve.ts","../src/graphic/shape/Droplet.ts","../src/graphic/shape/Heart.ts","../src/graphic/shape/Isogon.ts","../src/graphic/shape/Ring.ts","../src/graphic/shape/Rose.ts","../src/graphic/shape/Star.ts","../src/graphic/shape/Trochoid.ts","../src/graphic/Pattern.ts","../src/core/OrientedBoundingRect.ts","../src/debug/showDebugDirtyRect.ts","../src/canvas/helper.ts","../src/canvas/dashStyle.ts","../src/canvas/graphic.ts","../src/canvas/Layer.ts","../src/canvas/Painter.ts","../src/svg/SVGPathRebuilder.ts","../src/svg/mapStyleToAttrs.ts","../src/svg/core.ts","../src/svg/cssClassId.ts","../src/svg/cssAnimation.ts","../src/svg/cssEmphasis.ts","../src/svg/graphic.ts","../src/svg/domapi.ts","../src/svg/patch.ts","../src/svg/Painter.ts","../src/all.ts"],"sourcesContent":["declare const wx: {\n getSystemInfoSync: Function\n};\n\nclass Browser {\n firefox = false\n ie = false\n edge = false\n newEdge = false\n weChat = false\n version: string | number\n}\n\nclass Env {\n browser = new Browser()\n node = false\n wxa = false\n worker = false\n\n svgSupported = false\n touchEventsSupported = false\n pointerEventsSupported = false\n domSupported = false\n transformSupported = false\n transform3dSupported = false\n\n hasGlobalWindow = typeof window !== 'undefined'\n}\n\nconst env = new Env();\n\nif (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {\n env.wxa = true;\n env.touchEventsSupported = true;\n}\nelse if (typeof document === 'undefined' && typeof self !== 'undefined') {\n // In worker\n env.worker = true;\n}\nelse if (!env.hasGlobalWindow || 'Deno' in window) {\n // In node\n env.node = true;\n env.svgSupported = true;\n}\nelse {\n detect(navigator.userAgent, env);\n}\n\n// Zepto.js\n// (c) 2010-2013 Thomas Fuchs\n// Zepto.js may be freely distributed under the MIT license.\n\nfunction detect(ua: string, env: Env) {\n const browser = env.browser;\n const firefox = ua.match(/Firefox\\/([\\d.]+)/);\n const ie = ua.match(/MSIE\\s([\\d.]+)/)\n // IE 11 Trident/7.0; rv:11.0\n || ua.match(/Trident\\/.+?rv:(([\\d.]+))/);\n const edge = ua.match(/Edge?\\/([\\d.]+)/); // IE 12 and 12+\n\n const weChat = (/micromessenger/i).test(ua);\n\n if (firefox) {\n browser.firefox = true;\n browser.version = firefox[1];\n }\n if (ie) {\n browser.ie = true;\n browser.version = ie[1];\n }\n\n if (edge) {\n browser.edge = true;\n browser.version = edge[1];\n browser.newEdge = +edge[1].split('.')[0] > 18;\n }\n\n // It is difficult to detect WeChat in Win Phone precisely, because ua can\n // not be set on win phone. So we do not consider Win Phone.\n if (weChat) {\n browser.weChat = true;\n }\n\n env.svgSupported = typeof SVGRect !== 'undefined';\n env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge;\n env.pointerEventsSupported = 'onpointerdown' in window\n && (browser.edge || (browser.ie && +browser.version >= 11));\n env.domSupported = typeof document !== 'undefined';\n\n const style = document.documentElement.style;\n\n env.transform3dSupported = (\n // IE9 only supports transform 2D\n // transform 3D supported since IE10\n // we detect it by whether 'transition' is in style\n (browser.ie && 'transition' in style)\n // edge\n || browser.edge\n // webkit\n || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix()))\n // gecko-based browsers\n || 'MozPerspective' in style\n ) // Opera supports CSS transforms after version 12\n && !('OTransition' in style);\n\n // except IE 6-8 and very old firefox 2-3 & opera 10.1\n // other browsers all support `transform`\n env.transformSupported = env.transform3dSupported\n // transform 2D is supported in IE9\n || (browser.ie && +browser.version >= 9);\n\n}\n\n\nexport default env;\n","export const DEFAULT_FONT_SIZE = 12;\nexport const DEFAULT_FONT_FAMILY = 'sans-serif';\nexport const DEFAULT_FONT = `${DEFAULT_FONT_SIZE}px ${DEFAULT_FONT_FAMILY}`;\n\ninterface Platform {\n // TODO CanvasLike?\n createCanvas(): HTMLCanvasElement\n measureText(text: string, font?: string): { width: number }\n loadImage(\n src: string,\n onload: () => void | HTMLImageElement['onload'],\n onerror: () => void | HTMLImageElement['onerror']\n ): HTMLImageElement\n}\n\n// Text width map used for environment there is no canvas\n// Only common ascii is used for size concern.\n\n// Generated from following code\n//\n// ctx.font = '12px sans-serif';\n// const asciiRange = [32, 126];\n// let mapStr = '';\n// for (let i = asciiRange[0]; i <= asciiRange[1]; i++) {\n// const char = String.fromCharCode(i);\n// const width = ctx.measureText(char).width;\n// const ratio = Math.round(width / 12 * 100);\n// mapStr += String.fromCharCode(ratio + 20))\n// }\n// mapStr.replace(/\\\\/g, '\\\\\\\\');\nconst OFFSET = 20;\nconst SCALE = 100;\n// TODO other basic fonts?\n// eslint-disable-next-line\nconst defaultWidthMapStr = `007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\\\\\WQb\\\\0FWLg\\\\bWb\\\\WQ\\\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\\\FFF5.5N`;\n\nfunction getTextWidthMap(mapStr: string): Record {\n const map: Record = {};\n if (typeof JSON === 'undefined') {\n return map;\n }\n for (let i = 0; i < mapStr.length; i++) {\n const char = String.fromCharCode(i + 32);\n const size = (mapStr.charCodeAt(i) - OFFSET) / SCALE;\n map[char] = size;\n }\n return map;\n}\n\nexport const DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr);\n\nexport const platformApi: Platform = {\n // Export methods\n createCanvas() {\n return typeof document !== 'undefined'\n && document.createElement('canvas');\n },\n\n measureText: (function () {\n\n let _ctx: CanvasRenderingContext2D;\n let _cachedFont: string;\n return (text: string, font?: string) => {\n if (!_ctx) {\n const canvas = platformApi.createCanvas();\n _ctx = canvas && canvas.getContext('2d');\n }\n if (_ctx) {\n if (_cachedFont !== font) {\n _cachedFont = _ctx.font = font || DEFAULT_FONT;\n }\n return _ctx.measureText(text);\n }\n else {\n text = text || '';\n font = font || DEFAULT_FONT;\n // Use font size if there is no other method can be used.\n const res = /((?:\\d+)?\\.?\\d*)px/.exec(font);\n const fontSize = res && +res[1] || DEFAULT_FONT_SIZE;\n let width = 0;\n if (font.indexOf('mono') >= 0) { // is monospace\n width = fontSize * text.length;\n }\n else {\n for (let i = 0; i < text.length; i++) {\n const preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]];\n width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize);\n }\n }\n return { width };\n }\n };\n })(),\n\n loadImage(src, onload, onerror) {\n const image = new Image();\n image.onload = onload;\n image.onerror = onerror;\n image.src = src;\n return image;\n }\n};\n\nexport function setPlatformAPI(newPlatformApis: Partial) {\n for (let key in platformApi) {\n // Don't assign unknown methods.\n if ((newPlatformApis as any)[key]) {\n (platformApi as any)[key] = (newPlatformApis as any)[key];\n }\n }\n}\n","/* global: defineProperty */\nimport { Dictionary, ArrayLike, KeyOfDistributive } from './types';\nimport { GradientObject } from '../graphic/Gradient';\nimport { ImagePatternObject } from '../graphic/Pattern';\nimport { platformApi } from './platform';\n\n// 用于处理merge时无法遍历Date等对象的问题\nconst BUILTIN_OBJECT: Record = reduce([\n 'Function',\n 'RegExp',\n 'Date',\n 'Error',\n 'CanvasGradient',\n 'CanvasPattern',\n // For node-canvas\n 'Image',\n 'Canvas'\n], (obj, val) => {\n obj['[object ' + val + ']'] = true;\n return obj;\n}, {} as Record);\n\nconst TYPED_ARRAY: Record = reduce([\n 'Int8',\n 'Uint8',\n 'Uint8Clamped',\n 'Int16',\n 'Uint16',\n 'Int32',\n 'Uint32',\n 'Float32',\n 'Float64'\n], (obj, val) => {\n obj['[object ' + val + 'Array]'] = true;\n return obj;\n}, {} as Record);\n\nconst objToString = Object.prototype.toString;\n\nconst arrayProto = Array.prototype;\nconst nativeForEach = arrayProto.forEach;\nconst nativeFilter = arrayProto.filter;\nconst nativeSlice = arrayProto.slice;\nconst nativeMap = arrayProto.map;\n// In case some env may redefine the global variable `Function`.\nconst ctorFunction = function () {}.constructor;\nconst protoFunction = ctorFunction ? ctorFunction.prototype : null;\nconst protoKey = '__proto__';\n\n\nlet idStart = 0x0907;\n\n/**\n * Generate unique id\n */\nexport function guid(): number {\n return idStart++;\n}\n\nexport function logError(...args: any[]) {\n if (typeof console !== 'undefined') {\n console.error.apply(console, args);\n }\n}\n/**\n * Those data types can be cloned:\n * Plain object, Array, TypedArray, number, string, null, undefined.\n * Those data types will be assigned using the original data:\n * BUILTIN_OBJECT\n * Instance of user defined class will be cloned to a plain object, without\n * properties in prototype.\n * Other data types is not supported (not sure what will happen).\n *\n * Caution: do not support clone Date, for performance consideration.\n * (There might be a large number of date in `series.data`).\n * So date should not be modified in and out of echarts.\n */\nexport function clone(source: T): T {\n if (source == null || typeof source !== 'object') {\n return source;\n }\n\n let result = source as any;\n const typeStr = objToString.call(source);\n\n if (typeStr === '[object Array]') {\n if (!isPrimitive(source)) {\n result = [] as any;\n for (let i = 0, len = (source as any[]).length; i < len; i++) {\n result[i] = clone((source as any[])[i]);\n }\n }\n }\n else if (TYPED_ARRAY[typeStr]) {\n if (!isPrimitive(source)) {\n /* eslint-disable-next-line */\n const Ctor = source.constructor as typeof Float32Array;\n if (Ctor.from) {\n result = Ctor.from(source as Float32Array);\n }\n else {\n result = new Ctor((source as Float32Array).length);\n for (let i = 0, len = (source as Float32Array).length; i < len; i++) {\n result[i] = (source as Float32Array)[i];\n }\n }\n }\n }\n else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {\n result = {} as any;\n for (let key in source) {\n // Check if key is __proto__ to avoid prototype pollution\n if (source.hasOwnProperty(key) && key !== protoKey) {\n result[key] = clone(source[key]);\n }\n }\n }\n\n return result;\n}\n\nexport function merge<\n T extends Dictionary,\n S extends Dictionary\n>(target: T, source: S, overwrite?: boolean): T & S;\nexport function merge<\n T extends any,\n S extends any\n>(target: T, source: S, overwrite?: boolean): T | S;\nexport function merge(target: any, source: any, overwrite?: boolean): any {\n // We should escapse that source is string\n // and enter for ... in ...\n if (!isObject(source) || !isObject(target)) {\n return overwrite ? clone(source) : target;\n }\n\n for (let key in source) {\n // Check if key is __proto__ to avoid prototype pollution\n if (source.hasOwnProperty(key) && key !== protoKey) {\n const targetProp = target[key];\n const sourceProp = source[key];\n\n if (isObject(sourceProp)\n && isObject(targetProp)\n && !isArray(sourceProp)\n && !isArray(targetProp)\n && !isDom(sourceProp)\n && !isDom(targetProp)\n && !isBuiltInObject(sourceProp)\n && !isBuiltInObject(targetProp)\n && !isPrimitive(sourceProp)\n && !isPrimitive(targetProp)\n ) {\n // 如果需要递归覆盖,就递归调用merge\n merge(targetProp, sourceProp, overwrite);\n }\n else if (overwrite || !(key in target)) {\n // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况\n // NOTE,在 target[key] 不存在的时候也是直接覆盖\n target[key] = clone(source[key]);\n }\n }\n }\n\n return target;\n}\n\n/**\n * @param targetAndSources The first item is target, and the rests are source.\n * @param overwrite\n * @return Merged result\n */\nexport function mergeAll(targetAndSources: any[], overwrite?: boolean): any {\n let result = targetAndSources[0];\n for (let i = 1, len = targetAndSources.length; i < len; i++) {\n result = merge(result, targetAndSources[i], overwrite);\n }\n return result;\n}\n\nexport function extend<\n T extends Dictionary,\n S extends Dictionary\n>(target: T, source: S): T & S {\n // @ts-ignore\n if (Object.assign) {\n // @ts-ignore\n Object.assign(target, source);\n }\n else {\n for (let key in source) {\n // Check if key is __proto__ to avoid prototype pollution\n if (source.hasOwnProperty(key) && key !== protoKey) {\n (target as S & T)[key] = (source as T & S)[key];\n }\n }\n }\n return target as T & S;\n}\n\nexport function defaults<\n T extends Dictionary,\n S extends Dictionary\n>(target: T, source: S, overlay?: boolean): T & S {\n const keysArr = keys(source);\n for (let i = 0, len = keysArr.length; i < len; i++) {\n let key = keysArr[i];\n if ((overlay ? source[key] != null : (target as T & S)[key] == null)) {\n (target as S & T)[key] = (source as T & S)[key];\n }\n }\n return target as T & S;\n}\n\n// Expose createCanvas in util for compatibility\nexport const createCanvas = platformApi.createCanvas;\n\n/**\n * 查询数组中元素的index\n */\nexport function indexOf(array: T[] | readonly T[] | ArrayLike, value: T): number {\n if (array) {\n if ((array as T[]).indexOf) {\n return (array as T[]).indexOf(value);\n }\n for (let i = 0, len = array.length; i < len; i++) {\n if (array[i] === value) {\n return i;\n }\n }\n }\n return -1;\n}\n\n/**\n * 构造类继承关系\n *\n * @param clazz 源类\n * @param baseClazz 基类\n */\nexport function inherits(clazz: Function, baseClazz: Function) {\n const clazzPrototype = clazz.prototype;\n function F() {}\n F.prototype = baseClazz.prototype;\n clazz.prototype = new (F as any)();\n\n for (let prop in clazzPrototype) {\n if (clazzPrototype.hasOwnProperty(prop)) {\n clazz.prototype[prop] = clazzPrototype[prop];\n }\n }\n clazz.prototype.constructor = clazz;\n (clazz as any).superClass = baseClazz;\n}\n\nexport function mixin(target: T | Function, source: S | Function, override?: boolean) {\n target = 'prototype' in target ? target.prototype : target;\n source = 'prototype' in source ? source.prototype : source;\n // If build target is ES6 class. prototype methods is not enumerable. Use getOwnPropertyNames instead\n // TODO: Determine if source is ES6 class?\n if (Object.getOwnPropertyNames) {\n const keyList = Object.getOwnPropertyNames(source);\n for (let i = 0; i < keyList.length; i++) {\n const key = keyList[i];\n if (key !== 'constructor') {\n if ((override ? (source as any)[key] != null : (target as any)[key] == null)) {\n (target as any)[key] = (source as any)[key];\n }\n }\n }\n }\n else {\n defaults(target, source, override);\n }\n}\n\n/**\n * Consider typed array.\n * @param data\n */\nexport function isArrayLike(data: any): data is ArrayLike {\n if (!data) {\n return false;\n }\n if (typeof data === 'string') {\n return false;\n }\n return typeof data.length === 'number';\n}\n\n/**\n * 数组或对象遍历\n */\nexport function each | any[] | readonly any[] | ArrayLike, Context>(\n arr: I,\n cb: (\n this: Context,\n // Use unknown to avoid to infer to \"any\", which may disable typo check.\n value: I extends (infer T)[] | readonly (infer T)[] | ArrayLike ? T\n // Use Dictionary may cause infer fail when I is an interface.\n // So here use a Record to infer type.\n : I extends Dictionary ? I extends Record ? T : unknown : unknown,\n index?: I extends any[] | readonly any[] | ArrayLike ? number : keyof I & string, // keyof Dictionary will return number | string\n arr?: I\n ) => void,\n context?: Context\n) {\n if (!(arr && cb)) {\n return;\n }\n if ((arr as any).forEach && (arr as any).forEach === nativeForEach) {\n (arr as any).forEach(cb, context);\n }\n else if (arr.length === +arr.length) {\n for (let i = 0, len = arr.length; i < len; i++) {\n // FIXME: should the elided item be travelled? like `[33,,55]`.\n cb.call(context, (arr as any[])[i], i as any, arr);\n }\n }\n else {\n for (let key in arr) {\n if (arr.hasOwnProperty(key)) {\n cb.call(context, (arr as Dictionary)[key], key as any, arr);\n }\n }\n }\n}\n\n/**\n * Array mapping.\n * @typeparam T Type in Array\n * @typeparam R Type Returned\n * @return Must be an array.\n */\nexport function map(\n arr: readonly T[],\n cb: (this: Context, val: T, index?: number, arr?: readonly T[]) => R,\n context?: Context\n): R[] {\n // Take the same behavior with lodash when !arr and !cb,\n // which might be some common sense.\n if (!arr) {\n return [];\n }\n if (!cb) {\n return slice(arr) as unknown[] as R[];\n }\n if (arr.map && arr.map === nativeMap) {\n return arr.map(cb, context);\n }\n else {\n const result = [];\n for (let i = 0, len = arr.length; i < len; i++) {\n // FIXME: should the elided item be travelled, like `[33,,55]`.\n result.push(cb.call(context, arr[i], i, arr));\n }\n return result;\n }\n}\n\nexport function reduce(\n arr: readonly T[],\n cb: (this: Context, previousValue: S, currentValue: T, currentIndex?: number, arr?: readonly T[]) => S,\n memo?: S,\n context?: Context\n): S {\n if (!(arr && cb)) {\n return;\n }\n for (let i = 0, len = arr.length; i < len; i++) {\n memo = cb.call(context, memo, arr[i], i, arr);\n }\n return memo;\n}\n\n/**\n * Array filtering.\n * @return Must be an array.\n */\nexport function filter(\n arr: readonly T[],\n cb: (this: Context, value: T, index: number, arr: readonly T[]) => boolean,\n context?: Context\n): T[] {\n // Take the same behavior with lodash when !arr and !cb,\n // which might be some common sense.\n if (!arr) {\n return [];\n }\n if (!cb) {\n return slice(arr);\n }\n if (arr.filter && arr.filter === nativeFilter) {\n return arr.filter(cb, context);\n }\n else {\n const result = [];\n for (let i = 0, len = arr.length; i < len; i++) {\n // FIXME: should the elided items be travelled? like `[33,,55]`.\n if (cb.call(context, arr[i], i, arr)) {\n result.push(arr[i]);\n }\n }\n return result;\n }\n}\n\n/**\n * 数组项查找\n */\nexport function find(\n arr: readonly T[],\n cb: (this: Context, value: T, index?: number, arr?: readonly T[]) => boolean,\n context?: Context\n): T {\n if (!(arr && cb)) {\n return;\n }\n for (let i = 0, len = arr.length; i < len; i++) {\n if (cb.call(context, arr[i], i, arr)) {\n return arr[i];\n }\n }\n}\n\n/**\n * Get all object keys\n *\n * Will return an empty array if obj is null/undefined\n */\nexport function keys(obj: T): (KeyOfDistributive & string)[] {\n if (!obj) {\n return [];\n }\n // Return type should be `keyof T` but exclude `number`, becuase\n // `Object.keys` only return string rather than `number | string`.\n type TKeys = KeyOfDistributive & string;\n if (Object.keys) {\n return Object.keys(obj) as TKeys[];\n }\n let keyList: TKeys[] = [];\n for (let key in obj) {\n if (obj.hasOwnProperty(key)) {\n keyList.push(key as any);\n }\n }\n return keyList;\n}\n\n// Remove this type in returned function. Or it will conflicts wicth callback with given context. Like Eventful.\n// According to lib.es5.d.ts\n/* eslint-disable max-len*/\nexport type Bind1 = F extends (this: Ctx, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Bind2 = F extends (this: Ctx, a: T1, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Bind3 = F extends (this: Ctx, a: T1, b: T2, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Bind4 = F extends (this: Ctx, a: T1, b: T2, c: T3, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Bind5 = F extends (this: Ctx, a: T1, b: T2, c: T3, d: T4, ...args: infer A) => infer R ? (...args: A) => R : unknown;\ntype BindFunc = (this: Ctx, ...arg: any[]) => any\n\ninterface FunctionBind {\n , Ctx>(func: F, ctx: Ctx): Bind1\n , Ctx, T1 extends Parameters[0]>(func: F, ctx: Ctx, a: T1): Bind2\n , Ctx, T1 extends Parameters[0], T2 extends Parameters[1]>(func: F, ctx: Ctx, a: T1, b: T2): Bind3\n , Ctx, T1 extends Parameters[0], T2 extends Parameters[1], T3 extends Parameters[2]>(func: F, ctx: Ctx, a: T1, b: T2, c: T3): Bind4\n , Ctx, T1 extends Parameters[0], T2 extends Parameters[1], T3 extends Parameters[2], T4 extends Parameters[3]>(func: F, ctx: Ctx, a: T1, b: T2, c: T3, d: T4): Bind5\n}\nfunction bindPolyfill any>(\n func: Fn, context: Ctx, ...args: any[]\n): (...args: Parameters) => ReturnType {\n return function (this: Ctx) {\n return func.apply(context, args.concat(nativeSlice.call(arguments)));\n };\n}\nexport const bind: FunctionBind = (protoFunction && isFunction(protoFunction.bind))\n ? protoFunction.call.bind(protoFunction.bind)\n : bindPolyfill;\n\nexport type Curry1 = F extends (a: T1, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Curry2 = F extends (a: T1, b: T2, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Curry3 = F extends (a: T1, b: T2, c: T3, ...args: infer A) => infer R ? (...args: A) => R : unknown;\nexport type Curry4 = F extends (a: T1, b: T2, c: T3, d: T4, ...args: infer A) => infer R ? (...args: A) => R : unknown;\ntype CurryFunc = (...arg: any[]) => any\n\nfunction curry[0]>(func: F, a: T1): Curry1\nfunction curry[0], T2 extends Parameters[1]>(func: F, a: T1, b: T2): Curry2\nfunction curry[0], T2 extends Parameters[1], T3 extends Parameters[2]>(func: F, a: T1, b: T2, c: T3): Curry3\nfunction curry[0], T2 extends Parameters[1], T3 extends Parameters[2], T4 extends Parameters[3]>(func: F, a: T1, b: T2, c: T3, d: T4): Curry4\nfunction curry(func: Function, ...args: any[]): Function {\n return function (this: any) {\n return func.apply(this, args.concat(nativeSlice.call(arguments)));\n };\n}\nexport {curry};\n/* eslint-enable max-len*/\n\nexport function isArray(value: any): value is any[] {\n if (Array.isArray) {\n return Array.isArray(value);\n }\n return objToString.call(value) === '[object Array]';\n}\n\nexport function isFunction(value: any): value is Function {\n return typeof value === 'function';\n}\n\nexport function isString(value: any): value is string {\n // Faster than `objToString.call` several times in chromium and webkit.\n // And `new String()` is rarely used.\n return typeof value === 'string';\n}\n\nexport function isStringSafe(value: any): value is string {\n return objToString.call(value) === '[object String]';\n}\n\nexport function isNumber(value: any): value is number {\n // Faster than `objToString.call` several times in chromium and webkit.\n // And `new Number()` is rarely used.\n return typeof value === 'number';\n}\n\n// Usage: `isObject(xxx)` or `isObject(SomeType)(xxx)`\n// Generic T can be used to avoid \"ts type gruards\" casting the `value` from its original\n// type `Object` implicitly so that loose its original type info in the subsequent code.\nexport function isObject(value: T): value is (object & T) {\n // Avoid a V8 JIT bug in Chrome 19-20.\n // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.\n const type = typeof value;\n return type === 'function' || (!!value && type === 'object');\n}\n\nexport function isBuiltInObject(value: any): boolean {\n return !!BUILTIN_OBJECT[objToString.call(value)];\n}\n\nexport function isTypedArray(value: any): boolean {\n return !!TYPED_ARRAY[objToString.call(value)];\n}\n\nexport function isDom(value: any): value is HTMLElement {\n return typeof value === 'object'\n && typeof value.nodeType === 'number'\n && typeof value.ownerDocument === 'object';\n}\n\nexport function isGradientObject(value: any): value is GradientObject {\n return (value as GradientObject).colorStops != null;\n}\n\nexport function isImagePatternObject(value: any): value is ImagePatternObject {\n return (value as ImagePatternObject).image != null;\n}\n\nexport function isRegExp(value: unknown): value is RegExp {\n return objToString.call(value) === '[object RegExp]';\n}\n\n/**\n * Whether is exactly NaN. Notice isNaN('a') returns true.\n */\nexport function eqNaN(value: any): boolean {\n /* eslint-disable-next-line no-self-compare */\n return value !== value;\n}\n\n/**\n * If value1 is not null, then return value1, otherwise judget rest of values.\n * Low performance.\n * @return Final value\n */\nexport function retrieve(...args: T[]): T {\n for (let i = 0, len = args.length; i < len; i++) {\n if (args[i] != null) {\n return args[i];\n }\n }\n}\n\nexport function retrieve2(value0: T, value1: R): T | R {\n return value0 != null\n ? value0\n : value1;\n}\n\nexport function retrieve3(value0: T, value1: R, value2: W): T | R | W {\n return value0 != null\n ? value0\n : value1 != null\n ? value1\n : value2;\n}\n\ntype SliceParams = Parameters;\nexport function slice(arr: ArrayLike, ...args: SliceParams): T[] {\n return nativeSlice.apply(arr, args as any[]);\n}\n\n/**\n * Normalize css liked array configuration\n * e.g.\n * 3 => [3, 3, 3, 3]\n * [4, 2] => [4, 2, 4, 2]\n * [4, 3, 2] => [4, 3, 2, 3]\n */\nexport function normalizeCssArray(val: number | number[]) {\n if (typeof (val) === 'number') {\n return [val, val, val, val];\n }\n const len = val.length;\n if (len === 2) {\n // vertical | horizontal\n return [val[0], val[1], val[0], val[1]];\n }\n else if (len === 3) {\n // top | horizontal | bottom\n return [val[0], val[1], val[2], val[1]];\n }\n return val;\n}\n\nexport function assert(condition: any, message?: string) {\n if (!condition) {\n throw new Error(message);\n }\n}\n\n/**\n * @param str string to be trimmed\n * @return trimmed string\n */\nexport function trim(str: string): string {\n if (str == null) {\n return null;\n }\n else if (typeof str.trim === 'function') {\n return str.trim();\n }\n else {\n return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g, '');\n }\n}\n\nconst primitiveKey = '__ec_primitive__';\n/**\n * Set an object as primitive to be ignored traversing children in clone or merge\n */\nexport function setAsPrimitive(obj: any) {\n obj[primitiveKey] = true;\n}\n\nexport function isPrimitive(obj: any): boolean {\n return obj[primitiveKey];\n}\n\ninterface MapInterface {\n delete(key: KEY): boolean;\n has(key: KEY): boolean;\n get(key: KEY): T | undefined;\n set(key: KEY, value: T): this;\n keys(): KEY[];\n forEach(callback: (value: T, key: KEY) => void): void;\n}\n\nclass MapPolyfill implements MapInterface {\n private data: Record = {} as Record;\n\n delete(key: KEY): boolean {\n const existed = this.has(key);\n if (existed) {\n delete this.data[key];\n }\n return existed;\n }\n has(key: KEY): boolean {\n return this.data.hasOwnProperty(key);\n }\n get(key: KEY): T | undefined {\n return this.data[key];\n }\n set(key: KEY, value: T): this {\n this.data[key] = value;\n return this;\n }\n keys(): KEY[] {\n return keys(this.data);\n }\n forEach(callback: (value: T, key: KEY) => void): void {\n // This is a potential performance bottleneck, see details in\n // https://github.com/ecomfe/zrender/issues/965, however it is now\n // less likely to occur as we default to native maps when possible.\n const data = this.data;\n for (const key in data) {\n if (data.hasOwnProperty(key)) {\n callback(data[key], key);\n }\n }\n }\n}\n\n// We want to use native Map if it is available, but we do not want to polyfill the global scope\n// in case users ship their own polyfills or patch the native map object in any way.\nconst isNativeMapSupported = typeof Map === 'function';\nfunction maybeNativeMap(): MapInterface {\n // Map may be a native class if we are running in an ES6 compatible environment.\n // eslint-disable-next-line\n return (isNativeMapSupported ? new Map() : new MapPolyfill()) as MapInterface;\n}\n\n/**\n * @constructor\n * @param {Object} obj\n */\nexport class HashMap {\n data: MapInterface\n\n constructor(obj?: HashMap | { [key in KEY]?: T } | KEY[]) {\n const isArr = isArray(obj);\n // Key should not be set on this, otherwise\n // methods get/set/... may be overridden.\n this.data = maybeNativeMap();\n const thisMap = this;\n\n (obj instanceof HashMap)\n ? obj.each(visit)\n : (obj && each(obj, visit));\n\n function visit(value: any, key: any) {\n isArr ? thisMap.set(value, key) : thisMap.set(key, value);\n }\n }\n\n // `hasKey` instead of `has` for potential misleading.\n hasKey(key: KEY): boolean {\n return this.data.has(key);\n }\n get(key: KEY): T {\n return this.data.get(key);\n }\n set(key: KEY, value: T): T {\n // Comparing with invocation chaining, `return value` is more commonly\n // used in this case: `const someVal = map.set('a', genVal());`\n this.data.set(key, value);\n return value;\n }\n // Although util.each can be performed on this hashMap directly, user\n // should not use the exposed keys, who are prefixed.\n each(\n cb: (this: Context, value?: T, key?: KEY) => void,\n context?: Context\n ) {\n this.data.forEach((value, key) => {\n cb.call(context, value, key);\n });\n }\n keys(): KEY[] {\n const keys = this.data.keys();\n return isNativeMapSupported\n // Native map returns an iterator so we need to convert it to an array\n ? Array.from(keys)\n : keys;\n }\n // Do not use this method if performance sensitive.\n removeKey(key: KEY): void {\n this.data.delete(key);\n }\n}\n\nexport function createHashMap(\n obj?: HashMap | { [key in KEY]?: T } | KEY[]\n) {\n return new HashMap(obj);\n}\n\nexport function concatArray(a: ArrayLike, b: ArrayLike): ArrayLike {\n const newArray = new (a as any).constructor(a.length + b.length);\n for (let i = 0; i < a.length; i++) {\n newArray[i] = a[i];\n }\n const offset = a.length;\n for (let i = 0; i < b.length; i++) {\n newArray[i + offset] = b[i];\n }\n return newArray;\n}\n\nexport function createObject(proto?: object, properties?: T): T {\n // Performance of Object.create\n // https://jsperf.com/style-strategy-proto-or-others\n let obj: T;\n if (Object.create) {\n obj = Object.create(proto);\n }\n else {\n const StyleCtor = function () {};\n StyleCtor.prototype = proto;\n obj = new (StyleCtor as any)();\n }\n if (properties) {\n extend(obj, properties);\n }\n\n return obj;\n}\n\n\nexport function disableUserSelect(dom: HTMLElement) {\n const domStyle = dom.style;\n domStyle.webkitUserSelect = 'none';\n domStyle.userSelect = 'none';\n // @ts-ignore\n domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)';\n (domStyle as any)['-webkit-touch-callout'] = 'none';\n}\n\nexport function hasOwn(own: object, prop: string): boolean {\n return own.hasOwnProperty(prop);\n}\n\nexport function noop() {}\n\nexport const RADIAN_TO_DEGREE = 180 / Math.PI;","/**\r\n * @deprecated\r\n * Use zrender.Point class instead\r\n */\r\nimport { MatrixArray } from './matrix';\r\n\r\n/* global Float32Array */\r\n\r\n// const ArrayCtor = typeof Float32Array === 'undefined'\r\n// ? Array\r\n// : Float32Array;\r\n\r\nexport type VectorArray = number[]\r\n/**\r\n * 创建一个向量\r\n */\r\nexport function create(x?: number, y?: number): VectorArray {\r\n if (x == null) {\r\n x = 0;\r\n }\r\n if (y == null) {\r\n y = 0;\r\n }\r\n return [x, y];\r\n}\r\n\r\n/**\r\n * 复制向量数据\r\n */\r\nexport function copy(out: T, v: VectorArray): T {\r\n out[0] = v[0];\r\n out[1] = v[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 克隆一个向量\r\n */\r\nexport function clone(v: VectorArray): VectorArray {\r\n return [v[0], v[1]];\r\n}\r\n\r\n/**\r\n * 设置向量的两个项\r\n */\r\nexport function set(out: T, a: number, b: number): T {\r\n out[0] = a;\r\n out[1] = b;\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量相加\r\n */\r\nexport function add(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = v1[0] + v2[0];\r\n out[1] = v1[1] + v2[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量缩放后相加\r\n */\r\nexport function scaleAndAdd(out: T, v1: VectorArray, v2: VectorArray, a: number): T {\r\n out[0] = v1[0] + v2[0] * a;\r\n out[1] = v1[1] + v2[1] * a;\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量相减\r\n */\r\nexport function sub(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = v1[0] - v2[0];\r\n out[1] = v1[1] - v2[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量长度\r\n */\r\nexport function len(v: VectorArray): number {\r\n return Math.sqrt(lenSquare(v));\r\n}\r\nexport const length = len;\r\n\r\n/**\r\n * 向量长度平方\r\n */\r\nexport function lenSquare(v: VectorArray): number {\r\n return v[0] * v[0] + v[1] * v[1];\r\n}\r\nexport const lengthSquare = lenSquare;\r\n\r\n/**\r\n * 向量乘法\r\n */\r\nexport function mul(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = v1[0] * v2[0];\r\n out[1] = v1[1] * v2[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量除法\r\n */\r\nexport function div(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = v1[0] / v2[0];\r\n out[1] = v1[1] / v2[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量点乘\r\n */\r\nexport function dot(v1: VectorArray, v2: VectorArray) {\r\n return v1[0] * v2[0] + v1[1] * v2[1];\r\n}\r\n\r\n/**\r\n * 向量缩放\r\n */\r\nexport function scale(out: T, v: VectorArray, s: number): T {\r\n out[0] = v[0] * s;\r\n out[1] = v[1] * s;\r\n return out;\r\n}\r\n\r\n/**\r\n * 向量归一化\r\n */\r\nexport function normalize(out: T, v: VectorArray): T {\r\n const d = len(v);\r\n if (d === 0) {\r\n out[0] = 0;\r\n out[1] = 0;\r\n }\r\n else {\r\n out[0] = v[0] / d;\r\n out[1] = v[1] / d;\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * 计算向量间距离\r\n */\r\nexport function distance(v1: VectorArray, v2: VectorArray): number {\r\n return Math.sqrt(\r\n (v1[0] - v2[0]) * (v1[0] - v2[0])\r\n + (v1[1] - v2[1]) * (v1[1] - v2[1])\r\n );\r\n}\r\nexport const dist = distance;\r\n\r\n/**\r\n * 向量距离平方\r\n */\r\nexport function distanceSquare(v1: VectorArray, v2: VectorArray): number {\r\n return (v1[0] - v2[0]) * (v1[0] - v2[0])\r\n + (v1[1] - v2[1]) * (v1[1] - v2[1]);\r\n}\r\nexport const distSquare = distanceSquare;\r\n\r\n/**\r\n * 求负向量\r\n */\r\nexport function negate(out: T, v: VectorArray): T {\r\n out[0] = -v[0];\r\n out[1] = -v[1];\r\n return out;\r\n}\r\n\r\n/**\r\n * 插值两个点\r\n */\r\nexport function lerp(out: T, v1: VectorArray, v2: VectorArray, t: number): T {\r\n out[0] = v1[0] + t * (v2[0] - v1[0]);\r\n out[1] = v1[1] + t * (v2[1] - v1[1]);\r\n return out;\r\n}\r\n\r\n/**\r\n * 矩阵左乘向量\r\n */\r\nexport function applyTransform(out: T, v: VectorArray, m: MatrixArray): T {\r\n const x = v[0];\r\n const y = v[1];\r\n out[0] = m[0] * x + m[2] * y + m[4];\r\n out[1] = m[1] * x + m[3] * y + m[5];\r\n return out;\r\n}\r\n\r\n/**\r\n * 求两个向量最小值\r\n */\r\nexport function min(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = Math.min(v1[0], v2[0]);\r\n out[1] = Math.min(v1[1], v2[1]);\r\n return out;\r\n}\r\n\r\n/**\r\n * 求两个向量最大值\r\n */\r\nexport function max(out: T, v1: VectorArray, v2: VectorArray): T {\r\n out[0] = Math.max(v1[0], v2[0]);\r\n out[1] = Math.max(v1[1], v2[1]);\r\n return out;\r\n}\r\n","import Handler from '../Handler';\r\nimport Element, { ElementEvent } from '../Element';\r\nimport Displayable from '../graphic/Displayable';\r\n\r\nclass Param {\r\n\r\n target: Element\r\n topTarget: Element\r\n\r\n constructor(target: Element, e?: ElementEvent) {\r\n this.target = target;\r\n this.topTarget = e && e.topTarget;\r\n }\r\n}\r\n\r\n// FIXME Draggable on element which has parent rotation or scale\r\nexport default class Draggable {\r\n\r\n handler: Handler\r\n\r\n _draggingTarget: Element\r\n _dropTarget: Element\r\n\r\n _x: number\r\n _y: number\r\n\r\n constructor(handler: Handler) {\r\n this.handler = handler;\r\n\r\n handler.on('mousedown', this._dragStart, this);\r\n handler.on('mousemove', this._drag, this);\r\n handler.on('mouseup', this._dragEnd, this);\r\n // `mosuemove` and `mouseup` can be continue to fire when dragging.\r\n // See [DRAG_OUTSIDE] in `Handler.js`. So we do not need to trigger\r\n // `_dragEnd` when globalout. That would brings better user experience.\r\n // this.on('globalout', this._dragEnd, this);\r\n\r\n // this._dropTarget = null;\r\n // this._draggingTarget = null;\r\n\r\n // this._x = 0;\r\n // this._y = 0;\r\n }\r\n\r\n _dragStart(e: ElementEvent) {\r\n let draggingTarget = e.target;\r\n // Find if there is draggable in the ancestor\r\n while (draggingTarget && !draggingTarget.draggable) {\r\n draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget;\r\n }\r\n if (draggingTarget) {\r\n this._draggingTarget = draggingTarget;\r\n draggingTarget.dragging = true;\r\n this._x = e.offsetX;\r\n this._y = e.offsetY;\r\n\r\n this.handler.dispatchToElement(\r\n new Param(draggingTarget, e), 'dragstart', e.event\r\n );\r\n }\r\n }\r\n\r\n _drag(e: ElementEvent) {\r\n const draggingTarget = this._draggingTarget;\r\n if (draggingTarget) {\r\n\r\n const x = e.offsetX;\r\n const y = e.offsetY;\r\n\r\n const dx = x - this._x;\r\n const dy = y - this._y;\r\n this._x = x;\r\n this._y = y;\r\n\r\n draggingTarget.drift(dx, dy, e);\r\n this.handler.dispatchToElement(\r\n new Param(draggingTarget, e), 'drag', e.event\r\n );\r\n\r\n const dropTarget = this.handler.findHover(\r\n x, y, draggingTarget as Displayable // PENDING\r\n ).target;\r\n const lastDropTarget = this._dropTarget;\r\n this._dropTarget = dropTarget;\r\n\r\n if (draggingTarget !== dropTarget) {\r\n if (lastDropTarget && dropTarget !== lastDropTarget) {\r\n this.handler.dispatchToElement(\r\n new Param(lastDropTarget, e), 'dragleave', e.event\r\n );\r\n }\r\n if (dropTarget && dropTarget !== lastDropTarget) {\r\n this.handler.dispatchToElement(\r\n new Param(dropTarget, e), 'dragenter', e.event\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n _dragEnd(e: ElementEvent) {\r\n const draggingTarget = this._draggingTarget;\r\n\r\n if (draggingTarget) {\r\n draggingTarget.dragging = false;\r\n }\r\n\r\n this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event);\r\n\r\n if (this._dropTarget) {\r\n this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event);\r\n }\r\n\r\n this._draggingTarget = null;\r\n this._dropTarget = null;\r\n }\r\n\r\n}","import { Dictionary, WithThisType } from './types';\r\n\r\n// Return true to cancel bubble\r\nexport type EventCallbackSingleParam = EvtParam extends any\r\n ? (params: EvtParam) => boolean | void\r\n : never\r\n\r\nexport type EventCallback = EvtParams extends any[]\r\n ? (...args: EvtParams) => boolean | void\r\n : never\r\nexport type EventQuery = string | Object\r\n\r\ntype CbThis = unknown extends Ctx ? Impl : Ctx;\r\n\r\ntype EventHandler = {\r\n h: EventCallback\r\n ctx: CbThis\r\n query: EventQuery\r\n\r\n callAtLast: boolean\r\n}\r\n\r\ntype DefaultEventDefinition = Dictionary>;\r\n\r\nexport interface EventProcessor {\r\n normalizeQuery?: (query: EventQuery) => EventQuery\r\n filter?: (eventType: keyof EvtDef, query: EventQuery) => boolean\r\n afterTrigger?: (eventType: keyof EvtDef) => void\r\n}\r\n\r\n/**\r\n * Event dispatcher.\r\n *\r\n * Event can be defined in EvtDef to enable type check. For example:\r\n * ```ts\r\n * interface FooEvents {\r\n * // key: event name, value: the first event param in `trigger` and `callback`.\r\n * myevent: {\r\n * aa: string;\r\n * bb: number;\r\n * };\r\n * }\r\n * class Foo extends Eventful {\r\n * fn() {\r\n * // Type check of event name and the first event param is enabled here.\r\n * this.trigger('myevent', {aa: 'xx', bb: 3});\r\n * }\r\n * }\r\n * let foo = new Foo();\r\n * // Type check of event name and the first event param is enabled here.\r\n * foo.on('myevent', (eventParam) => { ... });\r\n * ```\r\n *\r\n * @param eventProcessor The object eventProcessor is the scope when\r\n * `eventProcessor.xxx` called.\r\n * @param eventProcessor.normalizeQuery\r\n * param: {string|Object} Raw query.\r\n * return: {string|Object} Normalized query.\r\n * @param eventProcessor.filter Event will be dispatched only\r\n * if it returns `true`.\r\n * param: {string} eventType\r\n * param: {string|Object} query\r\n * return: {boolean}\r\n * @param eventProcessor.afterTrigger Called after all handlers called.\r\n * param: {string} eventType\r\n */\r\nexport default class Eventful {\r\n\r\n private _$handlers: Dictionary[]>\r\n\r\n protected _$eventProcessor: EventProcessor\r\n\r\n constructor(eventProcessors?: EventProcessor) {\r\n if (eventProcessors) {\r\n this._$eventProcessor = eventProcessors;\r\n }\r\n }\r\n\r\n on(\r\n event: EvtNm,\r\n handler: WithThisType>,\r\n context?: Ctx\r\n ): this\r\n on(\r\n event: EvtNm,\r\n query: EventQuery,\r\n handler: WithThisType>,\r\n context?: Ctx\r\n ): this\r\n /**\r\n * Bind a handler.\r\n *\r\n * @param event The event name.\r\n * @param Condition used on event filter.\r\n * @param handler The event handler.\r\n * @param context\r\n */\r\n on(\r\n event: EvtNm,\r\n query: EventQuery | WithThisType, CbThis>,\r\n handler?: WithThisType, CbThis> | Ctx,\r\n context?: Ctx\r\n ): this {\r\n if (!this._$handlers) {\r\n this._$handlers = {};\r\n }\r\n\r\n const _h = this._$handlers;\r\n\r\n if (typeof query === 'function') {\r\n context = handler as Ctx;\r\n handler = query as (...args: any) => any;\r\n query = null;\r\n }\r\n\r\n if (!handler || !event) {\r\n return this;\r\n }\r\n\r\n const eventProcessor = this._$eventProcessor;\r\n if (query != null && eventProcessor && eventProcessor.normalizeQuery) {\r\n query = eventProcessor.normalizeQuery(query);\r\n }\r\n\r\n if (!_h[event as string]) {\r\n _h[event as string] = [];\r\n }\r\n\r\n for (let i = 0; i < _h[event as string].length; i++) {\r\n if (_h[event as string][i].h === handler) {\r\n return this;\r\n }\r\n }\r\n\r\n const wrap: EventHandler = {\r\n h: handler as EventCallback,\r\n query: query,\r\n ctx: (context || this) as CbThis,\r\n // FIXME\r\n // Do not publish this feature util it is proved that it makes sense.\r\n callAtLast: (handler as any).zrEventfulCallAtLast\r\n };\r\n\r\n const lastIndex = _h[event as string].length - 1;\r\n const lastWrap = _h[event as string][lastIndex];\r\n (lastWrap && lastWrap.callAtLast)\r\n ? _h[event as string].splice(lastIndex, 0, wrap)\r\n : _h[event as string].push(wrap);\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Whether any handler has bound.\r\n */\r\n isSilent(eventName: keyof EvtDef): boolean {\r\n const _h = this._$handlers;\r\n return !_h || !_h[eventName as string] || !_h[eventName as string].length;\r\n }\r\n\r\n /**\r\n * Unbind a event.\r\n *\r\n * @param eventType The event name.\r\n * If no `event` input, \"off\" all listeners.\r\n * @param handler The event handler.\r\n * If no `handler` input, \"off\" all listeners of the `event`.\r\n */\r\n off(eventType?: keyof EvtDef, handler?: Function): this {\r\n const _h = this._$handlers;\r\n\r\n if (!_h) {\r\n return this;\r\n }\r\n\r\n if (!eventType) {\r\n this._$handlers = {};\r\n return this;\r\n }\r\n\r\n if (handler) {\r\n if (_h[eventType as string]) {\r\n const newList = [];\r\n for (let i = 0, l = _h[eventType as string].length; i < l; i++) {\r\n if (_h[eventType as string][i].h !== handler) {\r\n newList.push(_h[eventType as string][i]);\r\n }\r\n }\r\n _h[eventType as string] = newList;\r\n }\r\n\r\n if (_h[eventType as string] && _h[eventType as string].length === 0) {\r\n delete _h[eventType as string];\r\n }\r\n }\r\n else {\r\n delete _h[eventType as string];\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Dispatch a event.\r\n *\r\n * @param {string} eventType The event name.\r\n */\r\n trigger(\r\n eventType: EvtNm,\r\n ...args: Parameters\r\n ): this {\r\n if (!this._$handlers) {\r\n return this;\r\n }\r\n\r\n const _h = this._$handlers[eventType as string];\r\n const eventProcessor = this._$eventProcessor;\r\n\r\n if (_h) {\r\n const argLen = args.length;\r\n\r\n const len = _h.length;\r\n for (let i = 0; i < len; i++) {\r\n const hItem = _h[i];\r\n if (eventProcessor\r\n && eventProcessor.filter\r\n && hItem.query != null\r\n && !eventProcessor.filter(eventType, hItem.query)\r\n ) {\r\n continue;\r\n }\r\n\r\n // Optimize advise from backbone\r\n switch (argLen) {\r\n case 0:\r\n hItem.h.call(hItem.ctx);\r\n break;\r\n case 1:\r\n hItem.h.call(hItem.ctx, args[0]);\r\n break;\r\n case 2:\r\n hItem.h.call(hItem.ctx, args[0], args[1]);\r\n break;\r\n default:\r\n // have more than 2 given arguments\r\n hItem.h.apply(hItem.ctx, args);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n eventProcessor && eventProcessor.afterTrigger\r\n && eventProcessor.afterTrigger(eventType);\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Dispatch a event with context, which is specified at the last parameter.\r\n *\r\n * @param {string} type The event name.\r\n */\r\n triggerWithContext(type: keyof EvtDef, ...args: any[]): this {\r\n if (!this._$handlers) {\r\n return this;\r\n }\r\n\r\n const _h = this._$handlers[type as string];\r\n const eventProcessor = this._$eventProcessor;\r\n\r\n if (_h) {\r\n const argLen = args.length;\r\n const ctx = args[argLen - 1];\r\n\r\n const len = _h.length;\r\n for (let i = 0; i < len; i++) {\r\n const hItem = _h[i];\r\n if (eventProcessor\r\n && eventProcessor.filter\r\n && hItem.query != null\r\n && !eventProcessor.filter(type, hItem.query)\r\n ) {\r\n continue;\r\n }\r\n\r\n // Optimize advise from backbone\r\n switch (argLen) {\r\n case 0:\r\n hItem.h.call(ctx);\r\n break;\r\n case 1:\r\n hItem.h.call(ctx, args[0]);\r\n break;\r\n case 2:\r\n hItem.h.call(ctx, args[0], args[1]);\r\n break;\r\n default:\r\n // have more than 2 given arguments\r\n hItem.h.apply(ctx, args.slice(1, argLen - 1));\r\n break;\r\n }\r\n }\r\n }\r\n\r\n eventProcessor && eventProcessor.afterTrigger\r\n && eventProcessor.afterTrigger(type);\r\n\r\n return this;\r\n }\r\n\r\n}\r\n","/**\r\n * The algoritm is learnt from\r\n * https://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/\r\n * And we made some optimization for matrix inversion.\r\n * Other similar approaches:\r\n * \"cv::getPerspectiveTransform\", \"Direct Linear Transformation\".\r\n */\r\n\r\nconst LN2 = Math.log(2);\r\n\r\nfunction determinant(\r\n rows: number[][],\r\n rank: number,\r\n rowStart: number,\r\n rowMask: number,\r\n colMask: number,\r\n detCache: {[key: string]: number}\r\n) {\r\n const cacheKey = rowMask + '-' + colMask;\r\n const fullRank = rows.length;\r\n\r\n if (detCache.hasOwnProperty(cacheKey)) {\r\n return detCache[cacheKey];\r\n }\r\n\r\n if (rank === 1) {\r\n // In this case the colMask must be like: `11101111`. We can find the place of `0`.\r\n const colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);\r\n return rows[rowStart][colStart];\r\n }\r\n\r\n const subRowMask = rowMask | (1 << rowStart);\r\n let subRowStart = rowStart + 1;\r\n while (rowMask & (1 << subRowStart)) {\r\n subRowStart++;\r\n }\r\n\r\n let sum = 0;\r\n for (let j = 0, colLocalIdx = 0; j < fullRank; j++) {\r\n const colTag = 1 << j;\r\n if (!(colTag & colMask)) {\r\n sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]\r\n // det(subMatrix(0, j))\r\n * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);\r\n colLocalIdx++;\r\n }\r\n }\r\n\r\n detCache[cacheKey] = sum;\r\n\r\n return sum;\r\n}\r\n\r\n/**\r\n * Usage:\r\n * ```js\r\n * const transformer = buildTransformer(\r\n * [10, 44, 100, 44, 100, 300, 10, 300],\r\n * [50, 54, 130, 14, 140, 330, 14, 220]\r\n * );\r\n * const out = [];\r\n * transformer && transformer([11, 33], out);\r\n * ```\r\n *\r\n * Notice: `buildTransformer` may take more than 10ms in some Android device.\r\n *\r\n * @param src source four points, [x0, y0, x1, y1, x2, y2, x3, y3]\r\n * @param dest destination four points, [x0, y0, x1, y1, x2, y2, x3, y3]\r\n * @return transformer If fail, return null/undefined.\r\n */\r\nexport function buildTransformer(src: number[], dest: number[]) {\r\n const mA = [\r\n [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]],\r\n [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]],\r\n [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]],\r\n [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]],\r\n [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]],\r\n [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]],\r\n [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]],\r\n [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]]\r\n ];\r\n\r\n const detCache = {};\r\n const det = determinant(mA, 8, 0, 0, 0, detCache);\r\n if (det === 0) {\r\n // can not make transformer when and only when\r\n // any three of the markers are collinear.\r\n return;\r\n }\r\n\r\n // `invert(mA) * dest`, that is, `adj(mA) / det * dest`.\r\n const vh: number[] = [];\r\n for (let i = 0; i < 8; i++) {\r\n for (let j = 0; j < 8; j++) {\r\n vh[j] == null && (vh[j] = 0);\r\n vh[j] += ((i + j) % 2 ? -1 : 1)\r\n // det(subMatrix(i, j))\r\n * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)\r\n / det * dest[i];\r\n }\r\n }\r\n\r\n return function (out: number[], srcPointX: number, srcPointY: number) {\r\n const pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;\r\n out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;\r\n out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;\r\n };\r\n}\r\n","\r\nimport env from './env';\r\nimport {buildTransformer} from './fourPointsTransform';\r\nimport {Dictionary} from './types';\r\n\r\nconst EVENT_SAVED_PROP = '___zrEVENTSAVED';\r\nconst _calcOut: number[] = [];\r\n\r\ntype SavedInfo = {\r\n markers?: HTMLDivElement[]\r\n trans?: ReturnType\r\n invTrans?: ReturnType\r\n srcCoords?: number[]\r\n}\r\n\r\n/**\r\n * Transform \"local coord\" from `elFrom` to `elTarget`.\r\n * \"local coord\": the coord based on the input `el`. The origin point is at\r\n * the position of \"left: 0; top: 0;\" in the `el`.\r\n *\r\n * Support when CSS transform is used.\r\n *\r\n * Having the `out` (that is, `[outX, outY]`), we can create an DOM element\r\n * and set the CSS style as \"left: outX; top: outY;\" and append it to `elTarge`\r\n * to locate the element.\r\n *\r\n * For example, this code below positions a child of `document.body` on the event\r\n * point, no matter whether `body` has `margin`/`paddin`/`transfrom`/... :\r\n * ```js\r\n * transformLocalCoord(out, container, document.body, event.offsetX, event.offsetY);\r\n * if (!eqNaN(out[0])) {\r\n * // Then locate the tip element on the event point.\r\n * var tipEl = document.createElement('div');\r\n * tipEl.style.cssText = 'position: absolute; left:' + out[0] + ';top:' + out[1] + ';';\r\n * document.body.appendChild(tipEl);\r\n * }\r\n * ```\r\n *\r\n * Notice: In some env this method is not supported. If called, `out` will be `[NaN, NaN]`.\r\n *\r\n * @param {Array.} out [inX: number, inY: number] The output..\r\n * If can not transform, `out` will not be modified but return `false`.\r\n * @param {HTMLElement} elFrom The `[inX, inY]` is based on elFrom.\r\n * @param {HTMLElement} elTarget The `out` is based on elTarget.\r\n * @param {number} inX\r\n * @param {number} inY\r\n * @return {boolean} Whether transform successfully.\r\n */\r\nexport function transformLocalCoord(\r\n out: number[],\r\n elFrom: HTMLElement,\r\n elTarget: HTMLElement,\r\n inX: number,\r\n inY: number\r\n) {\r\n return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true)\r\n && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]);\r\n}\r\n\r\n/**\r\n * Transform between a \"viewport coord\" and a \"local coord\".\r\n * \"viewport coord\": the coord based on the left-top corner of the viewport\r\n * of the browser.\r\n * \"local coord\": the coord based on the input `el`. The origin point is at\r\n * the position of \"left: 0; top: 0;\" in the `el`.\r\n *\r\n * Support the case when CSS transform is used on el.\r\n *\r\n * @param out [inX: number, inY: number] The output. If `inverse: false`,\r\n * it represents \"local coord\", otherwise \"vireport coord\".\r\n * If can not transform, `out` will not be modified but return `false`.\r\n * @param el The \"local coord\" is based on the `el`, see comment above.\r\n * @param inX If `inverse: false`,\r\n * it represents \"vireport coord\", otherwise \"local coord\".\r\n * @param inY If `inverse: false`,\r\n * it represents \"vireport coord\", otherwise \"local coord\".\r\n * @param inverse\r\n * `true`: from \"viewport coord\" to \"local coord\".\r\n * `false`: from \"local coord\" to \"viewport coord\".\r\n * @return {boolean} Whether transform successfully.\r\n */\r\nexport function transformCoordWithViewport(\r\n out: number[],\r\n el: HTMLElement,\r\n inX: number,\r\n inY: number,\r\n inverse?: boolean\r\n) {\r\n if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {\r\n const saved = (el as any)[EVENT_SAVED_PROP] || ((el as any)[EVENT_SAVED_PROP] = {});\r\n const markers = prepareCoordMarkers(el, saved);\r\n const transformer = preparePointerTransformer(markers, saved, inverse);\r\n if (transformer) {\r\n transformer(out, inX, inY);\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\nfunction prepareCoordMarkers(el: HTMLElement, saved: SavedInfo) {\r\n let markers = saved.markers;\r\n if (markers) {\r\n return markers;\r\n }\r\n\r\n markers = saved.markers = [];\r\n const propLR = ['left', 'right'];\r\n const propTB = ['top', 'bottom'];\r\n\r\n for (let i = 0; i < 4; i++) {\r\n const marker = document.createElement('div');\r\n const stl = marker.style;\r\n const idxLR = i % 2;\r\n const idxTB = (i >> 1) % 2;\r\n stl.cssText = [\r\n 'position: absolute',\r\n 'visibility: hidden',\r\n 'padding: 0',\r\n 'margin: 0',\r\n 'border-width: 0',\r\n 'user-select: none',\r\n 'width:0',\r\n 'height:0',\r\n // 'width: 5px',\r\n // 'height: 5px',\r\n propLR[idxLR] + ':0',\r\n propTB[idxTB] + ':0',\r\n propLR[1 - idxLR] + ':auto',\r\n propTB[1 - idxTB] + ':auto',\r\n ''\r\n ].join('!important;');\r\n el.appendChild(marker);\r\n markers.push(marker);\r\n }\r\n\r\n return markers;\r\n}\r\n\r\nfunction preparePointerTransformer(markers: HTMLDivElement[], saved: SavedInfo, inverse?: boolean) {\r\n const transformerName: 'invTrans' | 'trans' = inverse ? 'invTrans' : 'trans';\r\n const transformer = saved[transformerName];\r\n const oldSrcCoords = saved.srcCoords;\r\n const srcCoords = [];\r\n const destCoords = [];\r\n let oldCoordTheSame = true;\r\n\r\n for (let i = 0; i < 4; i++) {\r\n const rect = markers[i].getBoundingClientRect();\r\n const ii = 2 * i;\r\n const x = rect.left;\r\n const y = rect.top;\r\n srcCoords.push(x, y);\r\n oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];\r\n destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);\r\n }\r\n // Cache to avoid time consuming of `buildTransformer`.\r\n return (oldCoordTheSame && transformer)\r\n ? transformer\r\n : (\r\n saved.srcCoords = srcCoords,\r\n saved[transformerName] = inverse\r\n ? buildTransformer(destCoords, srcCoords)\r\n : buildTransformer(srcCoords, destCoords)\r\n );\r\n}\r\n\r\nexport function isCanvasEl(el: HTMLElement): el is HTMLCanvasElement {\r\n return el.nodeName.toUpperCase() === 'CANVAS';\r\n}\r\n\r\nconst replaceReg = /([&<>\"'])/g;\r\nconst replaceMap: Dictionary = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n '\\'': '''\r\n};\r\n\r\nexport function encodeHTML(source: string): string {\r\n return source == null\r\n ? ''\r\n : (source + '').replace(replaceReg, function (str, c) {\r\n return replaceMap[c];\r\n });\r\n}\r\n","/**\n * Utilities for mouse or touch events.\n */\n\nimport Eventful from './Eventful';\nimport env from './env';\nimport { ZRRawEvent } from './types';\nimport {isCanvasEl, transformCoordWithViewport} from './dom';\n\nconst MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;\nconst _calcOut: number[] = [];\nconst firefoxNotSupportOffsetXY = env.browser.firefox\n // use offsetX/offsetY for Firefox >= 39\n // PENDING: consider Firefox for Android and Firefox OS? >= 43\n && +(env.browser.version as string).split('.')[0] < 39;\n\ntype FirefoxMouseEvent = {\n layerX: number\n layerY: number\n}\n\n\n/**\n * Get the `zrX` and `zrY`, which are relative to the top-left of\n * the input `el`.\n * CSS transform (2D & 3D) is supported.\n *\n * The strategy to fetch the coords:\n * + If `calculate` is not set as `true`, users of this method should\n * ensure that `el` is the same or the same size & location as `e.target`.\n * Otherwise the result coords are probably not expected. Because we\n * firstly try to get coords from e.offsetX/e.offsetY.\n * + If `calculate` is set as `true`, the input `el` can be any element\n * and we force to calculate the coords based on `el`.\n * + The input `el` should be positionable (not position:static).\n *\n * The force `calculate` can be used in case like:\n * When mousemove event triggered on ec tooltip, `e.target` is not `el`(zr painter.dom).\n *\n * @param el DOM element.\n * @param e Mouse event or touch event.\n * @param out Get `out.zrX` and `out.zrY` as the result.\n * @param calculate Whether to force calculate\n * the coordinates but not use ones provided by browser.\n */\nexport function clientToLocal(\n el: HTMLElement,\n e: ZRRawEvent | FirefoxMouseEvent | Touch,\n out: {zrX?: number, zrY?: number},\n calculate?: boolean\n) {\n out = out || {};\n\n // According to the W3C Working Draft, offsetX and offsetY should be relative\n // to the padding edge of the target element. The only browser using this convention\n // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does\n // not support the properties.\n // (see http://www.jacklmoore.com/notes/mouse-position/)\n // In zr painter.dom, padding edge equals to border edge.\n if (calculate) {\n calculateZrXY(el, e as ZRRawEvent, out);\n }\n // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned\n // ancestor element, so we should make sure el is positioned (e.g., not position:static).\n // BTW1, Webkit don't return the same results as FF in non-simple cases (like add\n // zoom-factor, overflow / opacity layers, transforms ...)\n // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.\n // \n // BTW3, In ff, offsetX/offsetY is always 0.\n else if (firefoxNotSupportOffsetXY\n && (e as FirefoxMouseEvent).layerX != null\n && (e as FirefoxMouseEvent).layerX !== (e as MouseEvent).offsetX\n ) {\n out.zrX = (e as FirefoxMouseEvent).layerX;\n out.zrY = (e as FirefoxMouseEvent).layerY;\n }\n // For IE6+, chrome, safari, opera, firefox >= 39\n else if ((e as MouseEvent).offsetX != null) {\n out.zrX = (e as MouseEvent).offsetX;\n out.zrY = (e as MouseEvent).offsetY;\n }\n // For some other device, e.g., IOS safari.\n else {\n calculateZrXY(el, e as ZRRawEvent, out);\n }\n\n return out;\n}\n\nfunction calculateZrXY(\n el: HTMLElement,\n e: ZRRawEvent,\n out: {zrX?: number, zrY?: number}\n) {\n // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect.\n if (env.domSupported && el.getBoundingClientRect) {\n const ex = (e as MouseEvent).clientX;\n const ey = (e as MouseEvent).clientY;\n\n if (isCanvasEl(el)) {\n // Original approach, which do not support CSS transform.\n // marker can not be locationed in a canvas container\n // (getBoundingClientRect is always 0). We do not support\n // that input a pre-created canvas to zr while using css\n // transform in iOS.\n const box = el.getBoundingClientRect();\n out.zrX = ex - box.left;\n out.zrY = ey - box.top;\n return;\n }\n else {\n if (transformCoordWithViewport(_calcOut, el, ex, ey)) {\n out.zrX = _calcOut[0];\n out.zrY = _calcOut[1];\n return;\n }\n }\n }\n out.zrX = out.zrY = 0;\n}\n\n/**\n * Find native event compat for legency IE.\n * Should be called at the begining of a native event listener.\n *\n * @param e Mouse event or touch event or pointer event.\n * For lagency IE, we use `window.event` is used.\n * @return The native event.\n */\nexport function getNativeEvent(e: ZRRawEvent): ZRRawEvent {\n return e\n || (window.event as any); // For IE\n}\n\n/**\n * Normalize the coordinates of the input event.\n *\n * Get the `e.zrX` and `e.zrY`, which are relative to the top-left of\n * the input `el`.\n * Get `e.zrDelta` if using mouse wheel.\n * Get `e.which`, see the comment inside this function.\n *\n * Do not calculate repeatly if `zrX` and `zrY` already exist.\n *\n * Notice: see comments in `clientToLocal`. check the relationship\n * between the result coords and the parameters `el` and `calculate`.\n *\n * @param el DOM element.\n * @param e See `getNativeEvent`.\n * @param calculate Whether to force calculate\n * the coordinates but not use ones provided by browser.\n * @return The normalized native UIEvent.\n */\nexport function normalizeEvent(\n el: HTMLElement,\n e: ZRRawEvent,\n calculate?: boolean\n) {\n\n e = getNativeEvent(e);\n\n if (e.zrX != null) {\n return e;\n }\n\n const eventType = e.type;\n const isTouch = eventType && eventType.indexOf('touch') >= 0;\n\n if (!isTouch) {\n clientToLocal(el, e, e, calculate);\n const wheelDelta = getWheelDeltaMayPolyfill(e);\n // FIXME: IE8- has \"wheelDeta\" in event \"mousewheel\" but hat different value (120 times)\n // with Chrome and Safari. It's not correct for zrender event but we left it as it was.\n e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3;\n }\n else {\n const touch = eventType !== 'touchend'\n ? (e).targetTouches[0]\n : (e).changedTouches[0];\n touch && clientToLocal(el, touch, e, calculate);\n }\n\n // Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;\n // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js\n // If e.which has been defined, it may be readonly,\n // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which\n const button = (e).button;\n if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) {\n (e as any).which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0)));\n }\n // [Caution]: `e.which` from browser is not always reliable. For example,\n // when press left button and `mousemove (pointermove)` in Edge, the `e.which`\n // is 65536 and the `e.button` is -1. But the `mouseup (pointerup)` and\n // `mousedown (pointerdown)` is the same as Chrome does.\n\n return e;\n}\n\n// TODO: also provide prop \"deltaX\" \"deltaY\" in zrender \"mousewheel\" event.\nfunction getWheelDeltaMayPolyfill(e: ZRRawEvent): number {\n // Although event \"wheel\" do not has the prop \"wheelDelta\" in spec,\n // agent like Chrome and Safari still provide \"wheelDelta\" like\n // event \"mousewheel\" did (perhaps for backward compat).\n // Since zrender has been using \"wheelDeta\" in zrender event \"mousewheel\".\n // we currently do not break it.\n // But event \"wheel\" in firefox do not has \"wheelDelta\", so we calculate\n // \"wheelDeta\" from \"deltaX\", \"deltaY\" (which is the props in spec).\n const rawWheelDelta = (e as any).wheelDelta;\n // Theroetically `e.wheelDelta` won't be 0 unless some day it has been deprecated\n // by agent like Chrome or Safari. So we also calculate it if rawWheelDelta is 0.\n if (rawWheelDelta) {\n return rawWheelDelta;\n }\n\n const deltaX = (e as any).deltaX;\n const deltaY = (e as any).deltaY;\n if (deltaX == null || deltaY == null) {\n return rawWheelDelta;\n }\n\n // Test in Chrome and Safari (MacOS):\n // The sign is corrent.\n // The abs value is 99% corrent (inconsist case only like 62~63, 125~126 ...)\n const delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX);\n const sign = deltaY > 0 ? -1\n : deltaY < 0 ? 1\n : deltaX > 0 ? -1\n : 1;\n return 3 * delta * sign;\n}\n\n\ntype AddEventListenerParams = Parameters\ntype RemoveEventListenerParams = Parameters\n/**\n * @param el\n * @param name\n * @param handler\n * @param opt If boolean, means `opt.capture`\n * @param opt.capture\n * @param opt.passive\n */\nexport function addEventListener(\n el: HTMLElement | HTMLDocument,\n name: AddEventListenerParams[0],\n handler: AddEventListenerParams[1],\n opt?: AddEventListenerParams[2]\n) {\n // Reproduct the console warning:\n // [Violation] Added non-passive event listener to a scroll-blocking event.\n // Consider marking event handler as 'passive' to make the page more responsive.\n // Just set console log level: verbose in chrome dev tool.\n // then the warning log will be printed when addEventListener called.\n // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md\n // We have not yet found a neat way to using passive. Because in zrender the dom event\n // listener delegate all of the upper events of element. Some of those events need\n // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts.\n // Before passive can be adopted, these issues should be considered:\n // (1) Whether and how a zrender user specifies an event listener passive. And by default,\n // passive or not.\n // (2) How to tread that some zrender event listener is passive, and some is not. If\n // we use other way but not preventDefault of mousewheel and touchmove, browser\n // compatibility should be handled.\n\n // const opts = (env.passiveSupported && name === 'mousewheel')\n // ? {passive: true}\n // // By default, the third param of el.addEventListener is `capture: false`.\n // : void 0;\n // el.addEventListener(name, handler /* , opts */);\n el.addEventListener(name, handler, opt);\n}\n\n/**\n * Parameter are the same as `addEventListener`.\n *\n * Notice that if a listener is registered twice, one with capture and one without,\n * remove each one separately. Removal of a capturing listener does not affect a\n * non-capturing version of the same listener, and vice versa.\n */\nexport function removeEventListener(\n el: HTMLElement | HTMLDocument,\n name: RemoveEventListenerParams[0],\n handler: RemoveEventListenerParams[1],\n opt: RemoveEventListenerParams[2]\n) {\n el.removeEventListener(name, handler, opt);\n}\n\n/**\n * preventDefault and stopPropagation.\n * Notice: do not use this method in zrender. It can only be\n * used by upper applications if necessary.\n *\n * @param {Event} e A mouse or touch event.\n */\nexport const stop = function (e: MouseEvent | TouchEvent | PointerEvent) {\n e.preventDefault();\n e.stopPropagation();\n e.cancelBubble = true;\n};\n\n/**\n * This method only works for mouseup and mousedown. The functionality is restricted\n * for fault tolerance, See the `e.which` compatibility above.\n *\n * params can be MouseEvent or ElementEvent\n */\nexport function isMiddleOrRightButtonOnMouseUpDown(e: { which: number }) {\n return e.which === 2 || e.which === 3;\n}\n\n// For backward compatibility\nexport {Eventful as Dispatcher};\n","/**\n * Only implements needed gestures for mobile.\n */\n\nimport * as eventUtil from './event';\nimport { ZRRawTouchEvent, ZRPinchEvent, Dictionary } from './types';\nimport Displayable from '../graphic/Displayable';\n\ninterface TrackItem {\n points: number[][]\n touches: Touch[]\n target: Displayable,\n event: ZRRawTouchEvent\n}\n\nexport class GestureMgr {\n\n private _track: TrackItem[] = []\n\n constructor() {}\n\n recognize(event: ZRRawTouchEvent, target: Displayable, root: HTMLElement) {\n this._doTrack(event, target, root);\n return this._recognize(event);\n }\n\n clear() {\n this._track.length = 0;\n return this;\n }\n\n _doTrack(event: ZRRawTouchEvent, target: Displayable, root: HTMLElement) {\n const touches = event.touches;\n\n if (!touches) {\n return;\n }\n\n const trackItem: TrackItem = {\n points: [],\n touches: [],\n target: target,\n event: event\n };\n\n for (let i = 0, len = touches.length; i < len; i++) {\n const touch = touches[i];\n const pos = eventUtil.clientToLocal(root, touch, {});\n trackItem.points.push([pos.zrX, pos.zrY]);\n trackItem.touches.push(touch);\n }\n\n this._track.push(trackItem);\n }\n\n _recognize(event: ZRRawTouchEvent) {\n for (let eventName in recognizers) {\n if (recognizers.hasOwnProperty(eventName)) {\n const gestureInfo = recognizers[eventName](this._track, event);\n if (gestureInfo) {\n return gestureInfo;\n }\n }\n }\n }\n}\n\nfunction dist(pointPair: number[][]): number {\n const dx = pointPair[1][0] - pointPair[0][0];\n const dy = pointPair[1][1] - pointPair[0][1];\n\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nfunction center(pointPair: number[][]): number[] {\n return [\n (pointPair[0][0] + pointPair[1][0]) / 2,\n (pointPair[0][1] + pointPair[1][1]) / 2\n ];\n}\n\ntype Recognizer = (tracks: TrackItem[], event: ZRRawTouchEvent) => {\n type: string\n target: Displayable\n event: ZRRawTouchEvent\n}\n\nconst recognizers: Dictionary = {\n\n pinch: function (tracks: TrackItem[], event: ZRRawTouchEvent) {\n const trackLen = tracks.length;\n\n if (!trackLen) {\n return;\n }\n\n const pinchEnd = (tracks[trackLen - 1] || {}).points;\n const pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd;\n\n if (pinchPre\n && pinchPre.length > 1\n && pinchEnd\n && pinchEnd.length > 1\n ) {\n let pinchScale = dist(pinchEnd) / dist(pinchPre);\n !isFinite(pinchScale) && (pinchScale = 1);\n\n (event as ZRPinchEvent).pinchScale = pinchScale;\n\n const pinchCenter = center(pinchEnd);\n (event as ZRPinchEvent).pinchX = pinchCenter[0];\n (event as ZRPinchEvent).pinchY = pinchCenter[1];\n\n return {\n type: 'pinch',\n target: tracks[0].target,\n event: event\n };\n }\n }\n\n // Only pinch currently.\n};","/**\n * 3x2矩阵操作类\n * @exports zrender/tool/matrix\n */\n\n/* global Float32Array */\n\nimport {VectorArray} from './vector';\n\nexport type MatrixArray = number[]\n/**\n * Create a identity matrix.\n */\nexport function create(): MatrixArray {\n return [1, 0, 0, 1, 0, 0];\n}\n\n/**\n * 设置矩阵为单位矩阵\n */\nexport function identity(out: MatrixArray): MatrixArray {\n out[0] = 1;\n out[1] = 0;\n out[2] = 0;\n out[3] = 1;\n out[4] = 0;\n out[5] = 0;\n return out;\n}\n\n/**\n * 复制矩阵\n */\nexport function copy(out: MatrixArray, m: MatrixArray): MatrixArray {\n out[0] = m[0];\n out[1] = m[1];\n out[2] = m[2];\n out[3] = m[3];\n out[4] = m[4];\n out[5] = m[5];\n return out;\n}\n\n/**\n * 矩阵相乘\n */\nexport function mul(out: MatrixArray, m1: MatrixArray, m2: MatrixArray): MatrixArray {\n // Consider matrix.mul(m, m2, m);\n // where out is the same as m2.\n // So use temp constiable to escape error.\n const out0 = m1[0] * m2[0] + m1[2] * m2[1];\n const out1 = m1[1] * m2[0] + m1[3] * m2[1];\n const out2 = m1[0] * m2[2] + m1[2] * m2[3];\n const out3 = m1[1] * m2[2] + m1[3] * m2[3];\n const out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4];\n const out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5];\n out[0] = out0;\n out[1] = out1;\n out[2] = out2;\n out[3] = out3;\n out[4] = out4;\n out[5] = out5;\n return out;\n}\n\n/**\n * 平移变换\n */\nexport function translate(out: MatrixArray, a: MatrixArray, v: VectorArray): MatrixArray {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n out[4] = a[4] + v[0];\n out[5] = a[5] + v[1];\n return out;\n}\n\n/**\n * 旋转变换\n */\nexport function rotate(\n out: MatrixArray,\n a: MatrixArray,\n rad: number,\n pivot: VectorArray = [0, 0]\n): MatrixArray {\n const aa = a[0];\n const ac = a[2];\n const atx = a[4];\n const ab = a[1];\n const ad = a[3];\n const aty = a[5];\n const st = Math.sin(rad);\n const ct = Math.cos(rad);\n\n out[0] = aa * ct + ab * st;\n out[1] = -aa * st + ab * ct;\n out[2] = ac * ct + ad * st;\n out[3] = -ac * st + ct * ad;\n out[4] = ct * (atx - pivot[0]) + st * (aty - pivot[1]) + pivot[0];\n out[5] = ct * (aty - pivot[1]) - st * (atx - pivot[0]) + pivot[1];\n return out;\n}\n\n/**\n * 缩放变换\n */\nexport function scale(out: MatrixArray, a: MatrixArray, v: VectorArray): MatrixArray {\n const vx = v[0];\n const vy = v[1];\n out[0] = a[0] * vx;\n out[1] = a[1] * vy;\n out[2] = a[2] * vx;\n out[3] = a[3] * vy;\n out[4] = a[4] * vx;\n out[5] = a[5] * vy;\n return out;\n}\n\n/**\n * 求逆矩阵\n */\nexport function invert(out: MatrixArray, a: MatrixArray): MatrixArray | null {\n\n const aa = a[0];\n const ac = a[2];\n const atx = a[4];\n const ab = a[1];\n const ad = a[3];\n const aty = a[5];\n\n let det = aa * ad - ab * ac;\n if (!det) {\n return null;\n }\n det = 1.0 / det;\n\n out[0] = ad * det;\n out[1] = -ab * det;\n out[2] = -ac * det;\n out[3] = aa * det;\n out[4] = (ac * aty - ad * atx) * det;\n out[5] = (ab * atx - aa * aty) * det;\n return out;\n}\n\n/**\n * Clone a new matrix.\n */\nexport function clone(a: MatrixArray): MatrixArray {\n const b = create();\n copy(b, a);\n return b;\n}\n","import { MatrixArray } from './matrix';\r\n\r\nexport interface PointLike {\r\n x: number\r\n y: number\r\n}\r\nexport default class Point {\r\n\r\n x: number\r\n\r\n y: number\r\n\r\n constructor(x?: number, y?: number) {\r\n this.x = x || 0;\r\n this.y = y || 0;\r\n }\r\n\r\n /**\r\n * Copy from another point\r\n */\r\n copy(other: PointLike) {\r\n this.x = other.x;\r\n this.y = other.y;\r\n return this;\r\n }\r\n\r\n /**\r\n * Clone a point\r\n */\r\n clone() {\r\n return new Point(this.x, this.y);\r\n }\r\n\r\n /**\r\n * Set x and y\r\n */\r\n set(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n return this;\r\n }\r\n\r\n /**\r\n * If equal to another point\r\n */\r\n equal(other: PointLike) {\r\n return other.x === this.x && other.y === this.y;\r\n }\r\n\r\n /**\r\n * Add another point\r\n */\r\n add(other: PointLike) {\r\n this.x += other.x;\r\n this.y += other.y;\r\n return this;\r\n }\r\n\r\n scale(scalar: number) {\r\n this.x *= scalar;\r\n this.y *= scalar;\r\n }\r\n\r\n scaleAndAdd(other: PointLike, scalar: number) {\r\n this.x += other.x * scalar;\r\n this.y += other.y * scalar;\r\n }\r\n\r\n /**\r\n * Sub another point\r\n */\r\n sub(other: PointLike) {\r\n this.x -= other.x;\r\n this.y -= other.y;\r\n return this;\r\n }\r\n\r\n /**\r\n * Dot product with other point\r\n */\r\n dot(other: PointLike) {\r\n return this.x * other.x + this.y * other.y;\r\n }\r\n\r\n /**\r\n * Get length of point\r\n */\r\n len() {\r\n return Math.sqrt(this.x * this.x + this.y * this.y);\r\n }\r\n\r\n /**\r\n * Get squared length\r\n */\r\n lenSquare() {\r\n return this.x * this.x + this.y * this.y;\r\n }\r\n\r\n /**\r\n * Normalize\r\n */\r\n normalize() {\r\n const len = this.len();\r\n this.x /= len;\r\n this.y /= len;\r\n return this;\r\n }\r\n\r\n /**\r\n * Distance to another point\r\n */\r\n distance(other: PointLike) {\r\n const dx = this.x - other.x;\r\n const dy = this.y - other.y;\r\n return Math.sqrt(dx * dx + dy * dy);\r\n }\r\n\r\n /**\r\n * Square distance to another point\r\n */\r\n distanceSquare(other: Point) {\r\n const dx = this.x - other.x;\r\n const dy = this.y - other.y;\r\n return dx * dx + dy * dy;\r\n }\r\n\r\n /**\r\n * Negate\r\n */\r\n negate() {\r\n this.x = -this.x;\r\n this.y = -this.y;\r\n return this;\r\n }\r\n\r\n /**\r\n * Apply a transform matrix array.\r\n */\r\n transform(m: MatrixArray) {\r\n if (!m) {\r\n return;\r\n }\r\n const x = this.x;\r\n const y = this.y;\r\n this.x = m[0] * x + m[2] * y + m[4];\r\n this.y = m[1] * x + m[3] * y + m[5];\r\n return this;\r\n }\r\n\r\n toArray(out: number[]) {\r\n out[0] = this.x;\r\n out[1] = this.y;\r\n return out;\r\n }\r\n\r\n fromArray(input: number[]) {\r\n this.x = input[0];\r\n this.y = input[1];\r\n }\r\n\r\n static set(p: PointLike, x: number, y: number) {\r\n p.x = x;\r\n p.y = y;\r\n }\r\n\r\n static copy(p: PointLike, p2: PointLike) {\r\n p.x = p2.x;\r\n p.y = p2.y;\r\n }\r\n\r\n static len(p: PointLike) {\r\n return Math.sqrt(p.x * p.x + p.y * p.y);\r\n }\r\n\r\n static lenSquare(p: PointLike) {\r\n return p.x * p.x + p.y * p.y;\r\n }\r\n\r\n static dot(p0: PointLike, p1: PointLike) {\r\n return p0.x * p1.x + p0.y * p1.y;\r\n }\r\n\r\n static add(out: PointLike, p0: PointLike, p1: PointLike) {\r\n out.x = p0.x + p1.x;\r\n out.y = p0.y + p1.y;\r\n }\r\n\r\n static sub(out: PointLike, p0: PointLike, p1: PointLike) {\r\n out.x = p0.x - p1.x;\r\n out.y = p0.y - p1.y;\r\n }\r\n\r\n static scale(out: PointLike, p0: PointLike, scalar: number) {\r\n out.x = p0.x * scalar;\r\n out.y = p0.y * scalar;\r\n }\r\n\r\n static scaleAndAdd(out: PointLike, p0: PointLike, p1: PointLike, scalar: number) {\r\n out.x = p0.x + p1.x * scalar;\r\n out.y = p0.y + p1.y * scalar;\r\n }\r\n\r\n static lerp(out: PointLike, p0: PointLike, p1: PointLike, t: number) {\r\n const onet = 1 - t;\r\n out.x = onet * p0.x + t * p1.x;\r\n out.y = onet * p0.y + t * p1.y;\r\n }\r\n}","/**\r\n * @module echarts/core/BoundingRect\r\n */\r\n\r\nimport * as matrix from './matrix';\r\nimport Point, { PointLike } from './Point';\r\n\r\nconst mathMin = Math.min;\r\nconst mathMax = Math.max;\r\n\r\nconst lt = new Point();\r\nconst rb = new Point();\r\nconst lb = new Point();\r\nconst rt = new Point();\r\n\r\nconst minTv = new Point();\r\nconst maxTv = new Point();\r\n\r\nclass BoundingRect {\r\n\r\n x: number\r\n y: number\r\n width: number\r\n height: number\r\n\r\n constructor(x: number, y: number, width: number, height: number) {\r\n if (width < 0) {\r\n x = x + width;\r\n width = -width;\r\n }\r\n if (height < 0) {\r\n y = y + height;\r\n height = -height;\r\n }\r\n\r\n this.x = x;\r\n this.y = y;\r\n this.width = width;\r\n this.height = height;\r\n }\r\n\r\n union(other: BoundingRect) {\r\n const x = mathMin(other.x, this.x);\r\n const y = mathMin(other.y, this.y);\r\n\r\n // If x is -Infinity and width is Infinity (like in the case of\r\n // IncrementalDisplayble), x + width would be NaN\r\n if (isFinite(this.x) && isFinite(this.width)) {\r\n this.width = mathMax(\r\n other.x + other.width,\r\n this.x + this.width\r\n ) - x;\r\n }\r\n else {\r\n this.width = other.width;\r\n }\r\n\r\n if (isFinite(this.y) && isFinite(this.height)) {\r\n this.height = mathMax(\r\n other.y + other.height,\r\n this.y + this.height\r\n ) - y;\r\n }\r\n else {\r\n this.height = other.height;\r\n }\r\n\r\n this.x = x;\r\n this.y = y;\r\n }\r\n\r\n applyTransform(m: matrix.MatrixArray) {\r\n BoundingRect.applyTransform(this, this, m);\r\n }\r\n\r\n calculateTransform(b: RectLike): matrix.MatrixArray {\r\n const a = this;\r\n const sx = b.width / a.width;\r\n const sy = b.height / a.height;\r\n\r\n const m = matrix.create();\r\n\r\n // 矩阵右乘\r\n matrix.translate(m, m, [-a.x, -a.y]);\r\n matrix.scale(m, m, [sx, sy]);\r\n matrix.translate(m, m, [b.x, b.y]);\r\n\r\n return m;\r\n }\r\n\r\n intersect(b: RectLike, mtv?: PointLike): boolean {\r\n if (!b) {\r\n return false;\r\n }\r\n\r\n if (!(b instanceof BoundingRect)) {\r\n // Normalize negative width/height.\r\n b = BoundingRect.create(b);\r\n }\r\n\r\n const a = this;\r\n const ax0 = a.x;\r\n const ax1 = a.x + a.width;\r\n const ay0 = a.y;\r\n const ay1 = a.y + a.height;\r\n\r\n const bx0 = b.x;\r\n const bx1 = b.x + b.width;\r\n const by0 = b.y;\r\n const by1 = b.y + b.height;\r\n\r\n let overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0);\r\n if (mtv) {\r\n let dMin = Infinity;\r\n let dMax = 0;\r\n const d0 = Math.abs(ax1 - bx0);\r\n const d1 = Math.abs(bx1 - ax0);\r\n const d2 = Math.abs(ay1 - by0);\r\n const d3 = Math.abs(by1 - ay0);\r\n const dx = Math.min(d0, d1);\r\n const dy = Math.min(d2, d3);\r\n // On x axis\r\n if (ax1 < bx0 || bx1 < ax0) {\r\n if (dx > dMax) {\r\n dMax = dx;\r\n if (d0 < d1) {\r\n Point.set(maxTv, -d0, 0); // b is on the right\r\n }\r\n else {\r\n Point.set(maxTv, d1, 0); // b is on the left\r\n }\r\n }\r\n }\r\n else {\r\n if (dx < dMin) {\r\n dMin = dx;\r\n if (d0 < d1) {\r\n Point.set(minTv, d0, 0); // b is on the right\r\n }\r\n else {\r\n Point.set(minTv, -d1, 0); // b is on the left\r\n }\r\n }\r\n }\r\n\r\n // On y axis\r\n if (ay1 < by0 || by1 < ay0) {\r\n if (dy > dMax) {\r\n dMax = dy;\r\n if (d2 < d3) {\r\n Point.set(maxTv, 0, -d2); // b is on the bottom(larger y)\r\n }\r\n else {\r\n Point.set(maxTv, 0, d3); // b is on the top(smaller y)\r\n }\r\n }\r\n }\r\n else {\r\n if (dx < dMin) {\r\n dMin = dx;\r\n if (d2 < d3) {\r\n Point.set(minTv, 0, d2); // b is on the bottom\r\n }\r\n else {\r\n Point.set(minTv, 0, -d3); // b is on the top\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (mtv) {\r\n Point.copy(mtv, overlap ? minTv : maxTv);\r\n }\r\n return overlap;\r\n }\r\n\r\n contain(x: number, y: number): boolean {\r\n const rect = this;\r\n return x >= rect.x\r\n && x <= (rect.x + rect.width)\r\n && y >= rect.y\r\n && y <= (rect.y + rect.height);\r\n }\r\n\r\n clone() {\r\n return new BoundingRect(this.x, this.y, this.width, this.height);\r\n }\r\n\r\n /**\r\n * Copy from another rect\r\n */\r\n copy(other: RectLike) {\r\n BoundingRect.copy(this, other);\r\n }\r\n\r\n plain(): RectLike {\r\n return {\r\n x: this.x,\r\n y: this.y,\r\n width: this.width,\r\n height: this.height\r\n };\r\n }\r\n\r\n /**\r\n * If not having NaN or Infinity with attributes\r\n */\r\n isFinite(): boolean {\r\n return isFinite(this.x)\r\n && isFinite(this.y)\r\n && isFinite(this.width)\r\n && isFinite(this.height);\r\n }\r\n\r\n isZero(): boolean {\r\n return this.width === 0 || this.height === 0;\r\n }\r\n\r\n static create(rect: RectLike): BoundingRect {\r\n return new BoundingRect(rect.x, rect.y, rect.width, rect.height);\r\n }\r\n\r\n static copy(target: RectLike, source: RectLike) {\r\n target.x = source.x;\r\n target.y = source.y;\r\n target.width = source.width;\r\n target.height = source.height;\r\n }\r\n\r\n static applyTransform(target: RectLike, source: RectLike, m: matrix.MatrixArray) {\r\n // In case usage like this\r\n // el.getBoundingRect().applyTransform(el.transform)\r\n // And element has no transform\r\n if (!m) {\r\n if (target !== source) {\r\n BoundingRect.copy(target, source);\r\n }\r\n return;\r\n }\r\n // Fast path when there is no rotation in matrix.\r\n if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) {\r\n const sx = m[0];\r\n const sy = m[3];\r\n const tx = m[4];\r\n const ty = m[5];\r\n target.x = source.x * sx + tx;\r\n target.y = source.y * sy + ty;\r\n target.width = source.width * sx;\r\n target.height = source.height * sy;\r\n if (target.width < 0) {\r\n target.x += target.width;\r\n target.width = -target.width;\r\n }\r\n if (target.height < 0) {\r\n target.y += target.height;\r\n target.height = -target.height;\r\n }\r\n return;\r\n }\r\n\r\n // source and target can be same instance.\r\n lt.x = lb.x = source.x;\r\n lt.y = rt.y = source.y;\r\n rb.x = rt.x = source.x + source.width;\r\n rb.y = lb.y = source.y + source.height;\r\n\r\n lt.transform(m);\r\n rt.transform(m);\r\n rb.transform(m);\r\n lb.transform(m);\r\n\r\n target.x = mathMin(lt.x, rb.x, lb.x, rt.x);\r\n target.y = mathMin(lt.y, rb.y, lb.y, rt.y);\r\n const maxX = mathMax(lt.x, rb.x, lb.x, rt.x);\r\n const maxY = mathMax(lt.y, rb.y, lb.y, rt.y);\r\n target.width = maxX - target.x;\r\n target.height = maxY - target.y;\r\n }\r\n}\r\n\r\n\r\nexport type RectLike = {\r\n x: number\r\n y: number\r\n width: number\r\n height: number\r\n}\r\n\r\nexport default BoundingRect;","import * as util from './core/util';\nimport * as vec2 from './core/vector';\nimport Draggable from './mixin/Draggable';\nimport Eventful from './core/Eventful';\nimport * as eventTool from './core/event';\nimport {GestureMgr} from './core/GestureMgr';\nimport Displayable from './graphic/Displayable';\nimport {PainterBase} from './PainterBase';\nimport HandlerDomProxy, { HandlerProxyInterface } from './dom/HandlerProxy';\nimport { ZRRawEvent, ZRPinchEvent, ElementEventName, ElementEventNameWithOn, ZRRawTouchEvent } from './core/types';\nimport Storage from './Storage';\nimport Element, {ElementEvent} from './Element';\nimport CanvasPainter from './canvas/Painter';\nimport BoundingRect from './core/BoundingRect';\n\n/**\n * [The interface between `Handler` and `HandlerProxy`]:\n *\n * The default `HandlerProxy` only support the common standard web environment\n * (e.g., standalone browser, headless browser, embed browser in mobild APP, ...).\n * But `HandlerProxy` can be replaced to support more non-standard environment\n * (e.g., mini app), or to support more feature that the default `HandlerProxy`\n * not provided (like echarts-gl did).\n * So the interface between `Handler` and `HandlerProxy` should be stable. Do not\n * make break changes util inevitable. The interface include the public methods\n * of `Handler` and the events listed in `handlerNames` below, by which `HandlerProxy`\n * drives `Handler`.\n */\n\n/**\n * [DRAG_OUTSIDE]:\n *\n * That is, triggering `mousemove` and `mouseup` event when the pointer is out of the\n * zrender area when dragging. That is important for the improvement of the user experience\n * when dragging something near the boundary without being terminated unexpectedly.\n *\n * We originally consider to introduce new events like `pagemovemove` and `pagemouseup`\n * to resolve this issue. But some drawbacks of it is described in\n * https://github.com/ecomfe/zrender/pull/536#issuecomment-560286899\n *\n * Instead, we referenced the specifications:\n * https://www.w3.org/TR/touch-events/#the-touchmove-event\n * https://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#event-type-mousemove\n * where the the mousemove/touchmove can be continue to fire if the user began a drag\n * operation and the pointer has left the boundary. (for the mouse event, browsers\n * only do it on `document` and when the pointer has left the boundary of the browser.)\n *\n * So the default `HandlerProxy` supports this feature similarly: if it is in the dragging\n * state (see `pointerCapture` in `HandlerProxy`), the `mousemove` and `mouseup` continue\n * to fire until release the pointer. That is implemented by listen to those event on\n * `document`.\n * If we implement some other `HandlerProxy` only for touch device, that would be easier.\n * The touch event support this feature by default.\n * The term \"pointer capture\" is from the spec:\n * https://www.w3.org/TR/pointerevents2/#idl-def-element-setpointercapture-pointerid\n *\n * Note:\n * There might be some cases that the mouse event can not be received on `document`.\n * For example,\n * (A) When `useCapture` is not supported and some user defined event listeners on the ancestor\n * of zr dom throw Error.\n * (B) When `useCapture` is not supported and some user defined event listeners on the ancestor of\n * zr dom call `stopPropagation`.\n * In these cases, the `mousemove` event might be keep triggering event when the mouse is released.\n * We try to reduce the side-effect in those cases, that is, use `isOutsideBoundary` to prevent\n * it from do anything (especially, `findHover`).\n * (`useCapture` mean, `addEvnetListener(listener, {capture: true})`, althought it may not be\n * suppported in some environments.)\n *\n * Note:\n * If `HandlerProxy` listens to `document` with `useCapture`, `HandlerProxy` needs to\n * prevent user-registered-handler from calling `stopPropagation` and `preventDefault`\n * when the `event.target` is not a zrender dom element. Otherwise the user-registered-handler\n * may be able to prevent other elements (that not relevant to zrender) in the web page from receiving\n * dom events.\n */\n\nconst SILENT = 'silent';\n\nfunction makeEventPacket(eveType: ElementEventName, targetInfo: {\n target?: Element\n topTarget?: Element\n}, event: ZRRawEvent): ElementEvent {\n return {\n type: eveType,\n event: event,\n // target can only be an element that is not silent.\n target: targetInfo.target,\n // topTarget can be a silent element.\n topTarget: targetInfo.topTarget,\n cancelBubble: false,\n offsetX: event.zrX,\n offsetY: event.zrY,\n gestureEvent: (event as ZRPinchEvent).gestureEvent,\n pinchX: (event as ZRPinchEvent).pinchX,\n pinchY: (event as ZRPinchEvent).pinchY,\n pinchScale: (event as ZRPinchEvent).pinchScale,\n wheelDelta: event.zrDelta,\n zrByTouch: event.zrByTouch,\n which: event.which,\n stop: stopEvent\n };\n}\n\nfunction stopEvent(this: ElementEvent) {\n eventTool.stop(this.event);\n}\n\nclass EmptyProxy extends Eventful {\n handler: Handler = null\n dispose() {}\n setCursor() {}\n}\n\nclass HoveredResult {\n x: number\n y: number\n target: Displayable\n topTarget: Displayable\n constructor(x?: number, y?: number) {\n this.x = x;\n this.y = y;\n }\n}\n\nconst handlerNames = [\n 'click', 'dblclick', 'mousewheel', 'mouseout',\n 'mouseup', 'mousedown', 'mousemove', 'contextmenu'\n];\n\ntype HandlerName = 'click' |'dblclick' |'mousewheel' |'mouseout' |\n 'mouseup' |'mousedown' |'mousemove' |'contextmenu';\n\nconst tmpRect = new BoundingRect(0, 0, 0, 0);\n\n// TODO draggable\nclass Handler extends Eventful {\n\n storage: Storage\n painter: PainterBase\n painterRoot: HTMLElement\n\n proxy: HandlerProxyInterface\n\n private _hovered = new HoveredResult(0, 0)\n\n private _gestureMgr: GestureMgr\n\n private _draggingMgr: Draggable\n\n private _pointerSize: number\n\n _downEl: Element\n _upEl: Element\n _downPoint: [number, number]\n\n constructor(\n storage: Storage,\n painter: PainterBase,\n proxy: HandlerProxyInterface,\n painterRoot: HTMLElement,\n pointerSize: number\n ) {\n super();\n\n this.storage = storage;\n\n this.painter = painter;\n\n this.painterRoot = painterRoot;\n\n this._pointerSize = pointerSize;\n\n proxy = proxy || new EmptyProxy();\n\n /**\n * Proxy of event. can be Dom, WebGLSurface, etc.\n */\n this.proxy = null;\n\n this.setHandlerProxy(proxy);\n\n this._draggingMgr = new Draggable(this);\n }\n\n setHandlerProxy(proxy: HandlerProxyInterface) {\n if (this.proxy) {\n this.proxy.dispose();\n }\n\n if (proxy) {\n util.each(handlerNames, function (name) {\n proxy.on && proxy.on(name, this[name as HandlerName], this);\n }, this);\n // Attach handler\n proxy.handler = this;\n }\n this.proxy = proxy;\n }\n\n mousemove(event: ZRRawEvent) {\n const x = event.zrX;\n const y = event.zrY;\n\n const isOutside = isOutsideBoundary(this, x, y);\n\n let lastHovered = this._hovered;\n let lastHoveredTarget = lastHovered.target;\n\n // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call\n // (like 'setOption' or 'dispatchAction') in event handlers, we should find\n // lastHovered again here. Otherwise 'mouseout' can not be triggered normally.\n // See #6198.\n if (lastHoveredTarget && !lastHoveredTarget.__zr) {\n lastHovered = this.findHover(lastHovered.x, lastHovered.y);\n lastHoveredTarget = lastHovered.target;\n }\n\n const hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y);\n const hoveredTarget = hovered.target;\n\n const proxy = this.proxy;\n proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default');\n\n // Mouse out on previous hovered element\n if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) {\n this.dispatchToElement(lastHovered, 'mouseout', event);\n }\n\n // Mouse moving on one element\n this.dispatchToElement(hovered, 'mousemove', event);\n\n // Mouse over on a new element\n if (hoveredTarget && hoveredTarget !== lastHoveredTarget) {\n this.dispatchToElement(hovered, 'mouseover', event);\n }\n }\n\n mouseout(event: ZRRawEvent) {\n const eventControl = event.zrEventControl;\n\n if (eventControl !== 'only_globalout') {\n this.dispatchToElement(this._hovered, 'mouseout', event);\n }\n\n if (eventControl !== 'no_globalout') {\n // FIXME: if the pointer moving from the extra doms to realy \"outside\",\n // the `globalout` should have been triggered. But currently not.\n this.trigger('globalout', {type: 'globalout', event: event});\n }\n }\n\n /**\n * Resize\n */\n resize() {\n this._hovered = new HoveredResult(0, 0);\n }\n\n /**\n * Dispatch event\n */\n dispatch(eventName: HandlerName, eventArgs?: any) {\n const handler = this[eventName];\n handler && handler.call(this, eventArgs);\n }\n\n /**\n * Dispose\n */\n dispose() {\n\n this.proxy.dispose();\n\n this.storage = null;\n this.proxy = null;\n this.painter = null;\n }\n\n /**\n * 设置默认的cursor style\n * @param cursorStyle 例如 crosshair,默认为 'default'\n */\n setCursorStyle(cursorStyle: string) {\n const proxy = this.proxy;\n proxy.setCursor && proxy.setCursor(cursorStyle);\n }\n\n /**\n * 事件分发代理\n *\n * @private\n * @param {Object} targetInfo {target, topTarget} 目标图形元素\n * @param {string} eventName 事件名称\n * @param {Object} event 事件对象\n */\n dispatchToElement(targetInfo: {\n target?: Element\n topTarget?: Element\n }, eventName: ElementEventName, event: ZRRawEvent) {\n\n targetInfo = targetInfo || {};\n\n let el = targetInfo.target as Element;\n if (el && el.silent) {\n return;\n }\n const eventKey = ('on' + eventName) as ElementEventNameWithOn;\n const eventPacket = makeEventPacket(eventName, targetInfo, event);\n\n while (el) {\n el[eventKey]\n && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket));\n\n el.trigger(eventName, eventPacket);\n\n // Bubble to the host if on the textContent.\n // PENDING\n el = el.__hostTarget ? el.__hostTarget : el.parent;\n\n if (eventPacket.cancelBubble) {\n break;\n }\n }\n\n if (!eventPacket.cancelBubble) {\n // 冒泡到顶级 zrender 对象\n this.trigger(eventName, eventPacket);\n // 分发事件到用户自定义层\n // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在\n if (this.painter && (this.painter as CanvasPainter).eachOtherLayer) {\n (this.painter as CanvasPainter).eachOtherLayer(function (layer) {\n if (typeof (layer[eventKey]) === 'function') {\n layer[eventKey].call(layer, eventPacket);\n }\n if (layer.trigger) {\n layer.trigger(eventName, eventPacket);\n }\n });\n }\n }\n }\n\n findHover(x: number, y: number, exclude?: Displayable): HoveredResult {\n const list = this.storage.getDisplayList();\n const out = new HoveredResult(x, y);\n setHoverTarget(list, out, x, y, exclude);\n\n if (this._pointerSize && !out.target) {\n /**\n * If no element at pointer position, check intersection with\n * elements with pointer enlarged by target size.\n */\n const candidates: Displayable[] = [];\n const pointerSize = this._pointerSize;\n const targetSizeHalf = pointerSize / 2;\n const pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize);\n\n for (let i = list.length - 1; i >= 0; i--) {\n const el = list[i];\n if (el !== exclude\n && !el.ignore\n && !el.ignoreCoarsePointer\n // If an element ignores, its textContent should also ignore.\n // TSpan's parent is not a Group but a ZRText.\n // See Text.js _getOrCreateChild\n && (!el.parent || !(el.parent as any).ignoreCoarsePointer)\n ) {\n tmpRect.copy(el.getBoundingRect());\n if (el.transform) {\n tmpRect.applyTransform(el.transform);\n }\n if (tmpRect.intersect(pointerRect)) {\n candidates.push(el);\n }\n }\n }\n\n /**\n * If there are elements whose bounding boxes are near the pointer,\n * use the most top one that intersects with the enlarged pointer.\n */\n if (candidates.length) {\n const rStep = 4;\n const thetaStep = Math.PI / 12;\n const PI2 = Math.PI * 2;\n for (let r = 0; r < targetSizeHalf; r += rStep) {\n for (let theta = 0; theta < PI2; theta += thetaStep) {\n const x1 = x + r * Math.cos(theta);\n const y1 = y + r * Math.sin(theta);\n setHoverTarget(candidates, out, x1, y1, exclude);\n if (out.target) {\n return out;\n }\n }\n }\n }\n }\n\n return out;\n }\n\n processGesture(event: ZRRawEvent, stage?: 'start' | 'end' | 'change') {\n if (!this._gestureMgr) {\n this._gestureMgr = new GestureMgr();\n }\n const gestureMgr = this._gestureMgr;\n\n stage === 'start' && gestureMgr.clear();\n\n const gestureInfo = gestureMgr.recognize(\n event as ZRRawTouchEvent,\n this.findHover(event.zrX, event.zrY, null).target,\n (this.proxy as HandlerDomProxy).dom\n );\n\n stage === 'end' && gestureMgr.clear();\n\n // Do not do any preventDefault here. Upper application do that if necessary.\n if (gestureInfo) {\n const type = gestureInfo.type;\n (event as ZRPinchEvent).gestureEvent = type;\n\n let res = new HoveredResult();\n res.target = gestureInfo.target;\n this.dispatchToElement(res, type as ElementEventName, gestureInfo.event as ZRRawEvent);\n }\n }\n\n click: (event: ZRRawEvent) => void\n mousedown: (event: ZRRawEvent) => void\n mouseup: (event: ZRRawEvent) => void\n mousewheel: (event: ZRRawEvent) => void\n dblclick: (event: ZRRawEvent) => void\n contextmenu: (event: ZRRawEvent) => void\n}\n\n// Common handlers\nutil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name: HandlerName) {\n Handler.prototype[name] = function (event) {\n const x = event.zrX;\n const y = event.zrY;\n const isOutside = isOutsideBoundary(this, x, y);\n\n let hovered;\n let hoveredTarget;\n\n if (name !== 'mouseup' || !isOutside) {\n // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover\n hovered = this.findHover(x, y);\n hoveredTarget = hovered.target;\n }\n\n if (name === 'mousedown') {\n this._downEl = hoveredTarget;\n this._downPoint = [event.zrX, event.zrY];\n // In case click triggered before mouseup\n this._upEl = hoveredTarget;\n }\n else if (name === 'mouseup') {\n this._upEl = hoveredTarget;\n }\n else if (name === 'click') {\n if (this._downEl !== this._upEl\n // Original click event is triggered on the whole canvas element,\n // including the case that `mousedown` - `mousemove` - `mouseup`,\n // which should be filtered, otherwise it will bring trouble to\n // pan and zoom.\n || !this._downPoint\n // Arbitrary value\n || vec2.dist(this._downPoint, [event.zrX, event.zrY]) > 4\n ) {\n return;\n }\n this._downPoint = null;\n }\n\n this.dispatchToElement(hovered, name, event);\n };\n});\n\nfunction isHover(displayable: Displayable, x: number, y: number) {\n if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) {\n let el: Element = displayable;\n let isSilent;\n let ignoreClip = false;\n while (el) {\n // Ignore clip on any ancestors.\n if (el.ignoreClip) {\n ignoreClip = true;\n }\n if (!ignoreClip) {\n let clipPath = el.getClipPath();\n // If clipped by ancestor.\n // FIXME: If clipPath has neither stroke nor fill,\n // el.clipPath.contain(x, y) will always return false.\n if (clipPath && !clipPath.contain(x, y)) {\n return false;\n }\n }\n if (el.silent) {\n isSilent = true;\n }\n // Consider when el is textContent, also need to be silent\n // if any of its host el and its ancestors is silent.\n const hostEl = el.__hostTarget;\n el = hostEl ? hostEl : el.parent;\n }\n return isSilent ? SILENT : true;\n }\n\n return false;\n}\n\nfunction setHoverTarget(\n list: Displayable[],\n out: HoveredResult,\n x: number,\n y: number,\n exclude: Displayable\n) {\n for (let i = list.length - 1; i >= 0; i--) {\n const el = list[i];\n let hoverCheckResult;\n if (el !== exclude\n // getDisplayList may include ignored item in VML mode\n && !el.ignore\n && (hoverCheckResult = isHover(el, x, y))\n ) {\n !out.topTarget && (out.topTarget = el);\n if (hoverCheckResult !== SILENT) {\n out.target = el;\n break;\n }\n }\n }\n}\n\n/**\n * See [DRAG_OUTSIDE].\n */\nfunction isOutsideBoundary(handlerInstance: Handler, x: number, y: number) {\n const painter = handlerInstance.painter;\n return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight();\n}\n\nexport default Handler;\n","// https://github.com/mziccard/node-timsort\nconst DEFAULT_MIN_MERGE = 32;\n\nconst DEFAULT_MIN_GALLOPING = 7;\n\ntype CompareFunc =(a: T, b: T) => number\n\nfunction minRunLength(n: number): number {\n var r = 0;\n\n while (n >= DEFAULT_MIN_MERGE) {\n r |= n & 1;\n n >>= 1;\n }\n\n return n + r;\n}\n\nfunction makeAscendingRun(array: T[], lo: number, hi: number, compare: CompareFunc) {\n var runHi = lo + 1;\n\n if (runHi === hi) {\n return 1;\n }\n\n if (compare(array[runHi++], array[lo]) < 0) {\n while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {\n runHi++;\n }\n\n reverseRun(array, lo, runHi);\n }\n else {\n while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {\n runHi++;\n }\n }\n\n return runHi - lo;\n}\n\nfunction reverseRun(array: T[], lo: number, hi: number) {\n hi--;\n\n while (lo < hi) {\n var t = array[lo];\n array[lo++] = array[hi];\n array[hi--] = t;\n }\n}\n\nfunction binaryInsertionSort(array: T[], lo: number, hi: number, start: number, compare: CompareFunc) {\n if (start === lo) {\n start++;\n }\n\n for (; start < hi; start++) {\n var pivot = array[start];\n\n var left = lo;\n var right = start;\n var mid;\n\n while (left < right) {\n mid = left + right >>> 1;\n\n if (compare(pivot, array[mid]) < 0) {\n right = mid;\n }\n else {\n left = mid + 1;\n }\n }\n\n var n = start - left;\n\n switch (n) {\n case 3:\n array[left + 3] = array[left + 2];\n\n case 2:\n array[left + 2] = array[left + 1];\n\n case 1:\n array[left + 1] = array[left];\n break;\n default:\n while (n > 0) {\n array[left + n] = array[left + n - 1];\n n--;\n }\n }\n\n array[left] = pivot;\n }\n}\n\nfunction gallopLeft(value: T, array: T[], start: number, length: number, hint: number, compare: CompareFunc) {\n var lastOffset = 0;\n var maxOffset = 0;\n var offset = 1;\n\n if (compare(value, array[start + hint]) > 0) {\n maxOffset = length - hint;\n\n while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n lastOffset += hint;\n offset += hint;\n }\n else {\n maxOffset = hint + 1;\n while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n var tmp = lastOffset;\n lastOffset = hint - offset;\n offset = hint - tmp;\n }\n\n lastOffset++;\n while (lastOffset < offset) {\n var m = lastOffset + (offset - lastOffset >>> 1);\n\n if (compare(value, array[start + m]) > 0) {\n lastOffset = m + 1;\n }\n else {\n offset = m;\n }\n }\n return offset;\n}\n\nfunction gallopRight(value: T, array: T[], start: number, length: number, hint: number, compare: CompareFunc) {\n var lastOffset = 0;\n var maxOffset = 0;\n var offset = 1;\n\n if (compare(value, array[start + hint]) < 0) {\n maxOffset = hint + 1;\n\n while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n var tmp = lastOffset;\n lastOffset = hint - offset;\n offset = hint - tmp;\n }\n else {\n maxOffset = length - hint;\n\n while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) {\n lastOffset = offset;\n offset = (offset << 1) + 1;\n\n if (offset <= 0) {\n offset = maxOffset;\n }\n }\n\n if (offset > maxOffset) {\n offset = maxOffset;\n }\n\n lastOffset += hint;\n offset += hint;\n }\n\n lastOffset++;\n\n while (lastOffset < offset) {\n var m = lastOffset + (offset - lastOffset >>> 1);\n\n if (compare(value, array[start + m]) < 0) {\n offset = m;\n }\n else {\n lastOffset = m + 1;\n }\n }\n\n return offset;\n}\n\nfunction TimSort(array: T[], compare: CompareFunc) {\n let minGallop = DEFAULT_MIN_GALLOPING;\n let runStart: number[];\n let runLength: number[];\n let stackSize = 0;\n\n var tmp: T[] = [];\n\n runStart = [];\n runLength = [];\n\n function pushRun(_runStart: number, _runLength: number) {\n runStart[stackSize] = _runStart;\n runLength[stackSize] = _runLength;\n stackSize += 1;\n }\n\n function mergeRuns() {\n while (stackSize > 1) {\n var n = stackSize - 2;\n\n if (\n (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1])\n || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])\n ) {\n if (runLength[n - 1] < runLength[n + 1]) {\n n--;\n }\n }\n else if (runLength[n] > runLength[n + 1]) {\n break;\n }\n mergeAt(n);\n }\n }\n\n function forceMergeRuns() {\n while (stackSize > 1) {\n var n = stackSize - 2;\n\n if (n > 0 && runLength[n - 1] < runLength[n + 1]) {\n n--;\n }\n\n mergeAt(n);\n }\n }\n\n function mergeAt(i: number) {\n var start1 = runStart[i];\n var length1 = runLength[i];\n var start2 = runStart[i + 1];\n var length2 = runLength[i + 1];\n\n runLength[i] = length1 + length2;\n\n if (i === stackSize - 3) {\n runStart[i + 1] = runStart[i + 2];\n runLength[i + 1] = runLength[i + 2];\n }\n\n stackSize--;\n\n var k = gallopRight(array[start2], array, start1, length1, 0, compare);\n start1 += k;\n length1 -= k;\n\n if (length1 === 0) {\n return;\n }\n\n length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare);\n\n if (length2 === 0) {\n return;\n }\n\n if (length1 <= length2) {\n mergeLow(start1, length1, start2, length2);\n }\n else {\n mergeHigh(start1, length1, start2, length2);\n }\n }\n\n function mergeLow(start1: number, length1: number, start2: number, length2: number) {\n var i = 0;\n\n for (i = 0; i < length1; i++) {\n tmp[i] = array[start1 + i];\n }\n\n var cursor1 = 0;\n var cursor2 = start2;\n var dest = start1;\n\n array[dest++] = array[cursor2++];\n\n if (--length2 === 0) {\n for (i = 0; i < length1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n return;\n }\n\n if (length1 === 1) {\n for (i = 0; i < length2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n array[dest + length2] = tmp[cursor1];\n return;\n }\n\n var _minGallop = minGallop;\n var count1;\n var count2;\n var exit;\n\n while (1) {\n count1 = 0;\n count2 = 0;\n exit = false;\n\n do {\n if (compare(array[cursor2], tmp[cursor1]) < 0) {\n array[dest++] = array[cursor2++];\n count2++;\n count1 = 0;\n\n if (--length2 === 0) {\n exit = true;\n break;\n }\n }\n else {\n array[dest++] = tmp[cursor1++];\n count1++;\n count2 = 0;\n if (--length1 === 1) {\n exit = true;\n break;\n }\n }\n } while ((count1 | count2) < _minGallop);\n\n if (exit) {\n break;\n }\n\n do {\n count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare);\n\n if (count1 !== 0) {\n for (i = 0; i < count1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n\n dest += count1;\n cursor1 += count1;\n length1 -= count1;\n if (length1 <= 1) {\n exit = true;\n break;\n }\n }\n\n array[dest++] = array[cursor2++];\n\n if (--length2 === 0) {\n exit = true;\n break;\n }\n\n count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare);\n\n if (count2 !== 0) {\n for (i = 0; i < count2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n\n dest += count2;\n cursor2 += count2;\n length2 -= count2;\n\n if (length2 === 0) {\n exit = true;\n break;\n }\n }\n array[dest++] = tmp[cursor1++];\n\n if (--length1 === 1) {\n exit = true;\n break;\n }\n\n _minGallop--;\n } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n\n if (exit) {\n break;\n }\n\n if (_minGallop < 0) {\n _minGallop = 0;\n }\n\n _minGallop += 2;\n }\n\n minGallop = _minGallop;\n\n minGallop < 1 && (minGallop = 1);\n\n if (length1 === 1) {\n for (i = 0; i < length2; i++) {\n array[dest + i] = array[cursor2 + i];\n }\n array[dest + length2] = tmp[cursor1];\n }\n else if (length1 === 0) {\n throw new Error();\n }\n else {\n for (i = 0; i < length1; i++) {\n array[dest + i] = tmp[cursor1 + i];\n }\n }\n }\n\n function mergeHigh(start1: number, length1: number, start2: number, length2: number) {\n var i = 0;\n\n for (i = 0; i < length2; i++) {\n tmp[i] = array[start2 + i];\n }\n\n var cursor1 = start1 + length1 - 1;\n var cursor2 = length2 - 1;\n var dest = start2 + length2 - 1;\n var customCursor = 0;\n var customDest = 0;\n\n array[dest--] = array[cursor1--];\n\n if (--length1 === 0) {\n customCursor = dest - (length2 - 1);\n\n for (i = 0; i < length2; i++) {\n array[customCursor + i] = tmp[i];\n }\n\n return;\n }\n\n if (length2 === 1) {\n dest -= length1;\n cursor1 -= length1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = length1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n array[dest] = tmp[cursor2];\n return;\n }\n\n var _minGallop = minGallop;\n\n while (true) {\n var count1 = 0;\n var count2 = 0;\n var exit = false;\n\n do {\n if (compare(tmp[cursor2], array[cursor1]) < 0) {\n array[dest--] = array[cursor1--];\n count1++;\n count2 = 0;\n if (--length1 === 0) {\n exit = true;\n break;\n }\n }\n else {\n array[dest--] = tmp[cursor2--];\n count2++;\n count1 = 0;\n if (--length2 === 1) {\n exit = true;\n break;\n }\n }\n } while ((count1 | count2) < _minGallop);\n\n if (exit) {\n break;\n }\n\n do {\n count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare);\n\n if (count1 !== 0) {\n dest -= count1;\n cursor1 -= count1;\n length1 -= count1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = count1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n if (length1 === 0) {\n exit = true;\n break;\n }\n }\n\n array[dest--] = tmp[cursor2--];\n\n if (--length2 === 1) {\n exit = true;\n break;\n }\n\n count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare);\n\n if (count2 !== 0) {\n dest -= count2;\n cursor2 -= count2;\n length2 -= count2;\n customDest = dest + 1;\n customCursor = cursor2 + 1;\n\n for (i = 0; i < count2; i++) {\n array[customDest + i] = tmp[customCursor + i];\n }\n\n if (length2 <= 1) {\n exit = true;\n break;\n }\n }\n\n array[dest--] = array[cursor1--];\n\n if (--length1 === 0) {\n exit = true;\n break;\n }\n\n _minGallop--;\n } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING);\n\n if (exit) {\n break;\n }\n\n if (_minGallop < 0) {\n _minGallop = 0;\n }\n\n _minGallop += 2;\n }\n\n minGallop = _minGallop;\n\n if (minGallop < 1) {\n minGallop = 1;\n }\n\n if (length2 === 1) {\n dest -= length1;\n cursor1 -= length1;\n customDest = dest + 1;\n customCursor = cursor1 + 1;\n\n for (i = length1 - 1; i >= 0; i--) {\n array[customDest + i] = array[customCursor + i];\n }\n\n array[dest] = tmp[cursor2];\n }\n else if (length2 === 0) {\n throw new Error();\n // throw new Error('mergeHigh preconditions were not respected');\n }\n else {\n customCursor = dest - (length2 - 1);\n for (i = 0; i < length2; i++) {\n array[customCursor + i] = tmp[i];\n }\n }\n }\n\n return {\n mergeRuns,\n forceMergeRuns,\n pushRun\n };\n}\n\nexport default function sort(\n array: T[],\n compare: CompareFunc,\n lo?: number, hi?: number\n) {\n if (!lo) {\n lo = 0;\n }\n if (!hi) {\n hi = array.length;\n }\n\n var remaining = hi - lo;\n\n if (remaining < 2) {\n return;\n }\n\n var runLength = 0;\n\n if (remaining < DEFAULT_MIN_MERGE) {\n runLength = makeAscendingRun(array, lo, hi, compare);\n binaryInsertionSort(array, lo, hi, lo + runLength, compare);\n return;\n }\n\n var ts = TimSort(array, compare);\n\n var minRun = minRunLength(remaining);\n\n do {\n runLength = makeAscendingRun(array, lo, hi, compare);\n if (runLength < minRun) {\n var force = remaining;\n if (force > minRun) {\n force = minRun;\n }\n\n binaryInsertionSort(array, lo, lo + force, lo + runLength, compare);\n runLength = force;\n }\n\n ts.pushRun(lo, runLength);\n ts.mergeRuns();\n\n remaining -= runLength;\n lo += runLength;\n } while (remaining !== 0);\n\n ts.forceMergeRuns();\n}\n","// Bit masks to check which parts of element needs to be updated.\r\nexport const REDRAW_BIT = 1;\r\nexport const STYLE_CHANGED_BIT = 2;\r\nexport const SHAPE_CHANGED_BIT = 4;\r\n","import * as util from './core/util';\r\nimport Group, { GroupLike } from './graphic/Group';\r\nimport Element from './Element';\r\n\r\n// Use timsort because in most case elements are partially sorted\r\n// https://jsfiddle.net/pissang/jr4x7mdm/8/\r\nimport timsort from './core/timsort';\r\nimport Displayable from './graphic/Displayable';\r\nimport Path from './graphic/Path';\r\nimport { REDRAW_BIT } from './graphic/constants';\r\n\r\nlet invalidZErrorLogged = false;\r\nfunction logInvalidZError() {\r\n if (invalidZErrorLogged) {\r\n return;\r\n }\r\n invalidZErrorLogged = true;\r\n console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors');\r\n}\r\n\r\nfunction shapeCompareFunc(a: Displayable, b: Displayable) {\r\n if (a.zlevel === b.zlevel) {\r\n if (a.z === b.z) {\r\n return a.z2 - b.z2;\r\n }\r\n return a.z - b.z;\r\n }\r\n return a.zlevel - b.zlevel;\r\n}\r\n\r\nexport default class Storage {\r\n\r\n private _roots: Element[] = []\r\n\r\n private _displayList: Displayable[] = []\r\n\r\n private _displayListLen = 0\r\n\r\n traverse(\r\n cb: (this: T, el: Element) => void,\r\n context?: T\r\n ) {\r\n for (let i = 0; i < this._roots.length; i++) {\r\n this._roots[i].traverse(cb, context);\r\n }\r\n }\r\n\r\n /**\r\n * get a list of elements to be rendered\r\n *\r\n * @param {boolean} update whether to update elements before return\r\n * @param {DisplayParams} params options\r\n * @return {Displayable[]} a list of elements\r\n */\r\n getDisplayList(update?: boolean, includeIgnore?: boolean): Displayable[] {\r\n includeIgnore = includeIgnore || false;\r\n const displayList = this._displayList;\r\n // If displaylist is not created yet. Update force\r\n if (update || !displayList.length) {\r\n this.updateDisplayList(includeIgnore);\r\n }\r\n return displayList;\r\n }\r\n\r\n /**\r\n * 更新图形的绘制队列。\r\n * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,\r\n * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列\r\n */\r\n updateDisplayList(includeIgnore?: boolean) {\r\n this._displayListLen = 0;\r\n\r\n const roots = this._roots;\r\n const displayList = this._displayList;\r\n for (let i = 0, len = roots.length; i < len; i++) {\r\n this._updateAndAddDisplayable(roots[i], null, includeIgnore);\r\n }\r\n\r\n displayList.length = this._displayListLen;\r\n\r\n timsort(displayList, shapeCompareFunc);\r\n }\r\n\r\n private _updateAndAddDisplayable(\r\n el: Element,\r\n clipPaths: Path[],\r\n includeIgnore?: boolean\r\n ) {\r\n if (el.ignore && !includeIgnore) {\r\n return;\r\n }\r\n\r\n el.beforeUpdate();\r\n el.update();\r\n el.afterUpdate();\r\n\r\n const userSetClipPath = el.getClipPath();\r\n\r\n if (el.ignoreClip) {\r\n clipPaths = null;\r\n }\r\n else if (userSetClipPath) {\r\n\r\n // FIXME 效率影响\r\n if (clipPaths) {\r\n clipPaths = clipPaths.slice();\r\n }\r\n else {\r\n clipPaths = [];\r\n }\r\n\r\n let currentClipPath = userSetClipPath;\r\n let parentClipPath = el;\r\n // Recursively add clip path\r\n while (currentClipPath) {\r\n // clipPath 的变换是基于使用这个 clipPath 的元素\r\n // TODO: parent should be group type.\r\n currentClipPath.parent = parentClipPath as Group;\r\n currentClipPath.updateTransform();\r\n\r\n clipPaths.push(currentClipPath);\r\n\r\n parentClipPath = currentClipPath;\r\n currentClipPath = currentClipPath.getClipPath();\r\n }\r\n }\r\n\r\n // ZRText and Group and combining morphing Path may use children\r\n if ((el as GroupLike).childrenRef) {\r\n const children = (el as GroupLike).childrenRef();\r\n\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i];\r\n\r\n // Force to mark as dirty if group is dirty\r\n if (el.__dirty) {\r\n child.__dirty |= REDRAW_BIT;\r\n }\r\n\r\n this._updateAndAddDisplayable(child, clipPaths, includeIgnore);\r\n }\r\n\r\n // Mark group clean here\r\n el.__dirty = 0;\r\n\r\n }\r\n else {\r\n const disp = el as Displayable;\r\n // Element is displayable\r\n if (clipPaths && clipPaths.length) {\r\n disp.__clipPaths = clipPaths;\r\n }\r\n else if (disp.__clipPaths && disp.__clipPaths.length > 0) {\r\n disp.__clipPaths = [];\r\n }\r\n\r\n // Avoid invalid z, z2, zlevel cause sorting error.\r\n if (isNaN(disp.z)) {\r\n logInvalidZError();\r\n disp.z = 0;\r\n }\r\n if (isNaN(disp.z2)) {\r\n logInvalidZError();\r\n disp.z2 = 0;\r\n }\r\n if (isNaN(disp.zlevel)) {\r\n logInvalidZError();\r\n disp.zlevel = 0;\r\n }\r\n\r\n this._displayList[this._displayListLen++] = disp;\r\n }\r\n\r\n // Add decal\r\n const decalEl = (el as Path).getDecalElement && (el as Path).getDecalElement();\r\n if (decalEl) {\r\n this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore);\r\n }\r\n\r\n // Add attached text element and guide line.\r\n const textGuide = el.getTextGuideLine();\r\n if (textGuide) {\r\n this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore);\r\n }\r\n\r\n const textEl = el.getTextContent();\r\n if (textEl) {\r\n this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore);\r\n }\r\n }\r\n\r\n /**\r\n * 添加图形(Displayable)或者组(Group)到根节点\r\n */\r\n addRoot(el: Element) {\r\n if (el.__zr && el.__zr.storage === this) {\r\n return;\r\n }\r\n\r\n this._roots.push(el);\r\n }\r\n\r\n /**\r\n * 删除指定的图形(Displayable)或者组(Group)\r\n * @param el\r\n */\r\n delRoot(el: Element | Element[]) {\r\n\r\n if (el instanceof Array) {\r\n for (let i = 0, l = el.length; i < l; i++) {\r\n this.delRoot(el[i]);\r\n }\r\n return;\r\n }\r\n\r\n const idx = util.indexOf(this._roots, el);\r\n if (idx >= 0) {\r\n this._roots.splice(idx, 1);\r\n }\r\n }\r\n\r\n delAllRoots() {\r\n this._roots = [];\r\n this._displayList = [];\r\n this._displayListLen = 0;\r\n\r\n return;\r\n }\r\n\r\n getRoots() {\r\n return this._roots;\r\n }\r\n\r\n /**\r\n * 清空并且释放Storage\r\n */\r\n dispose() {\r\n this._displayList = null;\r\n this._roots = null;\r\n }\r\n\r\n displayableSortFunc = shapeCompareFunc\r\n}","import env from '../core/env';\r\n\r\ntype RequestAnimationFrameType = typeof window.requestAnimationFrame\r\n\r\nlet requestAnimationFrame: RequestAnimationFrameType;\r\n\r\nrequestAnimationFrame = (\r\n\tenv.hasGlobalWindow\r\n\t\t&& (\r\n\t\t\t(window.requestAnimationFrame && window.requestAnimationFrame.bind(window))\r\n\t\t\t// https://github.com/ecomfe/zrender/issues/189#issuecomment-224919809\r\n\t\t\t|| ((window as any).msRequestAnimationFrame && (window as any).msRequestAnimationFrame.bind(window))\r\n\t\t\t|| (window as any).mozRequestAnimationFrame\r\n\t\t\t// @ts-ignore\r\n\t\t\t|| window.webkitRequestAnimationFrame\r\n\t\t)\r\n) || function (func: Parameters[0]): number {\r\n\treturn setTimeout(func, 16) as any;\r\n};\r\n\r\nexport default requestAnimationFrame;\r\n","/**\r\n * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js\r\n * @see http://sole.github.io/tween.js/examples/03_graphs.html\r\n * @exports zrender/animation/easing\r\n */\r\n\r\ntype easingFunc = (percent: number) => number;\r\n\r\nexport type AnimationEasing = keyof typeof easingFuncs | easingFunc;\r\n\r\nconst easingFuncs = {\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n linear(k: number) {\r\n return k;\r\n },\r\n\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quadraticIn(k: number) {\r\n return k * k;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quadraticOut(k: number) {\r\n return k * (2 - k);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quadraticInOut(k: number) {\r\n if ((k *= 2) < 1) {\r\n return 0.5 * k * k;\r\n }\r\n return -0.5 * (--k * (k - 2) - 1);\r\n },\r\n\r\n // 三次方的缓动(t^3)\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n cubicIn(k: number) {\r\n return k * k * k;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n cubicOut(k: number) {\r\n return --k * k * k + 1;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n cubicInOut(k: number) {\r\n if ((k *= 2) < 1) {\r\n return 0.5 * k * k * k;\r\n }\r\n return 0.5 * ((k -= 2) * k * k + 2);\r\n },\r\n\r\n // 四次方的缓动(t^4)\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quarticIn(k: number) {\r\n return k * k * k * k;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quarticOut(k: number) {\r\n return 1 - (--k * k * k * k);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quarticInOut(k: number) {\r\n if ((k *= 2) < 1) {\r\n return 0.5 * k * k * k * k;\r\n }\r\n return -0.5 * ((k -= 2) * k * k * k - 2);\r\n },\r\n\r\n // 五次方的缓动(t^5)\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quinticIn(k: number) {\r\n return k * k * k * k * k;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quinticOut(k: number) {\r\n return --k * k * k * k * k + 1;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n quinticInOut(k: number) {\r\n if ((k *= 2) < 1) {\r\n return 0.5 * k * k * k * k * k;\r\n }\r\n return 0.5 * ((k -= 2) * k * k * k * k + 2);\r\n },\r\n\r\n // 正弦曲线的缓动(sin(t))\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n sinusoidalIn(k: number) {\r\n return 1 - Math.cos(k * Math.PI / 2);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n sinusoidalOut(k: number) {\r\n return Math.sin(k * Math.PI / 2);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n sinusoidalInOut(k: number) {\r\n return 0.5 * (1 - Math.cos(Math.PI * k));\r\n },\r\n\r\n // 指数曲线的缓动(2^t)\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n exponentialIn(k: number) {\r\n return k === 0 ? 0 : Math.pow(1024, k - 1);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n exponentialOut(k: number) {\r\n return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n exponentialInOut(k: number) {\r\n if (k === 0) {\r\n return 0;\r\n }\r\n if (k === 1) {\r\n return 1;\r\n }\r\n if ((k *= 2) < 1) {\r\n return 0.5 * Math.pow(1024, k - 1);\r\n }\r\n return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2);\r\n },\r\n\r\n // 圆形曲线的缓动(sqrt(1-t^2))\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n circularIn(k: number) {\r\n return 1 - Math.sqrt(1 - k * k);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n circularOut(k: number) {\r\n return Math.sqrt(1 - (--k * k));\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n circularInOut(k: number) {\r\n if ((k *= 2) < 1) {\r\n return -0.5 * (Math.sqrt(1 - k * k) - 1);\r\n }\r\n return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);\r\n },\r\n\r\n // 创建类似于弹簧在停止前来回振荡的动画\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n elasticIn(k: number) {\r\n let s;\r\n let a = 0.1;\r\n let p = 0.4;\r\n if (k === 0) {\r\n return 0;\r\n }\r\n if (k === 1) {\r\n return 1;\r\n }\r\n if (!a || a < 1) {\r\n a = 1;\r\n s = p / 4;\r\n }\r\n else {\r\n s = p * Math.asin(1 / a) / (2 * Math.PI);\r\n }\r\n return -(a * Math.pow(2, 10 * (k -= 1))\r\n * Math.sin((k - s) * (2 * Math.PI) / p));\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n elasticOut(k: number) {\r\n let s;\r\n let a = 0.1;\r\n let p = 0.4;\r\n if (k === 0) {\r\n return 0;\r\n }\r\n if (k === 1) {\r\n return 1;\r\n }\r\n if (!a || a < 1) {\r\n a = 1;\r\n s = p / 4;\r\n }\r\n else {\r\n s = p * Math.asin(1 / a) / (2 * Math.PI);\r\n }\r\n return (a * Math.pow(2, -10 * k)\r\n * Math.sin((k - s) * (2 * Math.PI) / p) + 1);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n elasticInOut(k: number) {\r\n let s;\r\n let a = 0.1;\r\n let p = 0.4;\r\n if (k === 0) {\r\n return 0;\r\n }\r\n if (k === 1) {\r\n return 1;\r\n }\r\n if (!a || a < 1) {\r\n a = 1;\r\n s = p / 4;\r\n }\r\n else {\r\n s = p * Math.asin(1 / a) / (2 * Math.PI);\r\n }\r\n if ((k *= 2) < 1) {\r\n return -0.5 * (a * Math.pow(2, 10 * (k -= 1))\r\n * Math.sin((k - s) * (2 * Math.PI) / p));\r\n }\r\n return a * Math.pow(2, -10 * (k -= 1))\r\n * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1;\r\n\r\n },\r\n\r\n // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n backIn(k: number) {\r\n let s = 1.70158;\r\n return k * k * ((s + 1) * k - s);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n backOut(k: number) {\r\n let s = 1.70158;\r\n return --k * k * ((s + 1) * k + s) + 1;\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n backInOut(k: number) {\r\n let s = 1.70158 * 1.525;\r\n if ((k *= 2) < 1) {\r\n return 0.5 * (k * k * ((s + 1) * k - s));\r\n }\r\n return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);\r\n },\r\n\r\n // 创建弹跳效果\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n bounceIn(k: number) {\r\n return 1 - easingFuncs.bounceOut(1 - k);\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n bounceOut(k: number) {\r\n if (k < (1 / 2.75)) {\r\n return 7.5625 * k * k;\r\n }\r\n else if (k < (2 / 2.75)) {\r\n return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;\r\n }\r\n else if (k < (2.5 / 2.75)) {\r\n return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;\r\n }\r\n else {\r\n return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;\r\n }\r\n },\r\n /**\r\n * @param {number} k\r\n * @return {number}\r\n */\r\n bounceInOut(k: number) {\r\n if (k < 0.5) {\r\n return easingFuncs.bounceIn(k * 2) * 0.5;\r\n }\r\n return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5;\r\n }\r\n};\r\n\r\n\r\nexport default easingFuncs;","/**\r\n * 曲线辅助模块\r\n */\r\n\r\nimport {\r\n create as v2Create,\r\n distSquare as v2DistSquare,\r\n VectorArray\r\n} from './vector';\r\n\r\nconst mathPow = Math.pow;\r\nconst mathSqrt = Math.sqrt;\r\n\r\nconst EPSILON = 1e-8;\r\nconst EPSILON_NUMERIC = 1e-4;\r\n\r\nconst THREE_SQRT = mathSqrt(3);\r\nconst ONE_THIRD = 1 / 3;\r\n\r\n// 临时变量\r\nconst _v0 = v2Create();\r\nconst _v1 = v2Create();\r\nconst _v2 = v2Create();\r\n\r\nfunction isAroundZero(val: number) {\r\n return val > -EPSILON && val < EPSILON;\r\n}\r\nfunction isNotAroundZero(val: number) {\r\n return val > EPSILON || val < -EPSILON;\r\n}\r\n/**\r\n * 计算三次贝塞尔值\r\n */\r\nexport function cubicAt(p0: number, p1: number, p2: number, p3: number, t: number): number {\r\n const onet = 1 - t;\r\n return onet * onet * (onet * p0 + 3 * t * p1)\r\n + t * t * (t * p3 + 3 * onet * p2);\r\n}\r\n\r\n/**\r\n * 计算三次贝塞尔导数值\r\n */\r\nexport function cubicDerivativeAt(p0: number, p1: number, p2: number, p3: number, t: number): number {\r\n const onet = 1 - t;\r\n return 3 * (\r\n ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet\r\n + (p3 - p2) * t * t\r\n );\r\n}\r\n\r\n/**\r\n * 计算三次贝塞尔方程根,使用盛金公式\r\n */\r\nexport function cubicRootAt(p0: number, p1: number, p2: number, p3: number, val: number, roots: number[]): number {\r\n // Evaluate roots of cubic functions\r\n const a = p3 + 3 * (p1 - p2) - p0;\r\n const b = 3 * (p2 - p1 * 2 + p0);\r\n const c = 3 * (p1 - p0);\r\n const d = p0 - val;\r\n\r\n const A = b * b - 3 * a * c;\r\n const B = b * c - 9 * a * d;\r\n const C = c * c - 3 * b * d;\r\n\r\n let n = 0;\r\n\r\n if (isAroundZero(A) && isAroundZero(B)) {\r\n if (isAroundZero(b)) {\r\n roots[0] = 0;\r\n }\r\n else {\r\n const t1 = -c / b; //t1, t2, t3, b is not zero\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n }\r\n }\r\n else {\r\n const disc = B * B - 4 * A * C;\r\n\r\n if (isAroundZero(disc)) {\r\n const K = B / A;\r\n const t1 = -b / a + K; // t1, a is not zero\r\n const t2 = -K / 2; // t2, t3\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n if (t2 >= 0 && t2 <= 1) {\r\n roots[n++] = t2;\r\n }\r\n }\r\n else if (disc > 0) {\r\n const discSqrt = mathSqrt(disc);\r\n let Y1 = A * b + 1.5 * a * (-B + discSqrt);\r\n let Y2 = A * b + 1.5 * a * (-B - discSqrt);\r\n if (Y1 < 0) {\r\n Y1 = -mathPow(-Y1, ONE_THIRD);\r\n }\r\n else {\r\n Y1 = mathPow(Y1, ONE_THIRD);\r\n }\r\n if (Y2 < 0) {\r\n Y2 = -mathPow(-Y2, ONE_THIRD);\r\n }\r\n else {\r\n Y2 = mathPow(Y2, ONE_THIRD);\r\n }\r\n const t1 = (-b - (Y1 + Y2)) / (3 * a);\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n }\r\n else {\r\n const T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A));\r\n const theta = Math.acos(T) / 3;\r\n const ASqrt = mathSqrt(A);\r\n const tmp = Math.cos(theta);\r\n\r\n const t1 = (-b - 2 * ASqrt * tmp) / (3 * a);\r\n const t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a);\r\n const t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a);\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n if (t2 >= 0 && t2 <= 1) {\r\n roots[n++] = t2;\r\n }\r\n if (t3 >= 0 && t3 <= 1) {\r\n roots[n++] = t3;\r\n }\r\n }\r\n }\r\n return n;\r\n}\r\n\r\n/**\r\n * 计算三次贝塞尔方程极限值的位置\r\n * @return 有效数目\r\n */\r\nexport function cubicExtrema(p0: number, p1: number, p2: number, p3: number, extrema: number[]): number {\r\n const b = 6 * p2 - 12 * p1 + 6 * p0;\r\n const a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2;\r\n const c = 3 * p1 - 3 * p0;\r\n\r\n let n = 0;\r\n if (isAroundZero(a)) {\r\n if (isNotAroundZero(b)) {\r\n const t1 = -c / b;\r\n if (t1 >= 0 && t1 <= 1) {\r\n extrema[n++] = t1;\r\n }\r\n }\r\n }\r\n else {\r\n const disc = b * b - 4 * a * c;\r\n if (isAroundZero(disc)) {\r\n extrema[0] = -b / (2 * a);\r\n }\r\n else if (disc > 0) {\r\n const discSqrt = mathSqrt(disc);\r\n const t1 = (-b + discSqrt) / (2 * a);\r\n const t2 = (-b - discSqrt) / (2 * a);\r\n if (t1 >= 0 && t1 <= 1) {\r\n extrema[n++] = t1;\r\n }\r\n if (t2 >= 0 && t2 <= 1) {\r\n extrema[n++] = t2;\r\n }\r\n }\r\n }\r\n return n;\r\n}\r\n\r\n/**\r\n * 细分三次贝塞尔曲线\r\n */\r\nexport function cubicSubdivide(p0: number, p1: number, p2: number, p3: number, t: number, out: number[]) {\r\n const p01 = (p1 - p0) * t + p0;\r\n const p12 = (p2 - p1) * t + p1;\r\n const p23 = (p3 - p2) * t + p2;\r\n\r\n const p012 = (p12 - p01) * t + p01;\r\n const p123 = (p23 - p12) * t + p12;\r\n\r\n const p0123 = (p123 - p012) * t + p012;\r\n // Seg0\r\n out[0] = p0;\r\n out[1] = p01;\r\n out[2] = p012;\r\n out[3] = p0123;\r\n // Seg1\r\n out[4] = p0123;\r\n out[5] = p123;\r\n out[6] = p23;\r\n out[7] = p3;\r\n}\r\n\r\n/**\r\n * 投射点到三次贝塞尔曲线上,返回投射距离。\r\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\r\n */\r\nexport function cubicProjectPoint(\r\n x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number,\r\n x: number, y: number, out: VectorArray\r\n): number {\r\n // http://pomax.github.io/bezierinfo/#projections\r\n let t;\r\n let interval = 0.005;\r\n let d = Infinity;\r\n let prev;\r\n let next;\r\n let d1;\r\n let d2;\r\n\r\n _v0[0] = x;\r\n _v0[1] = y;\r\n\r\n // 先粗略估计一下可能的最小距离的 t 值\r\n // PENDING\r\n for (let _t = 0; _t < 1; _t += 0.05) {\r\n _v1[0] = cubicAt(x0, x1, x2, x3, _t);\r\n _v1[1] = cubicAt(y0, y1, y2, y3, _t);\r\n d1 = v2DistSquare(_v0, _v1);\r\n if (d1 < d) {\r\n t = _t;\r\n d = d1;\r\n }\r\n }\r\n d = Infinity;\r\n\r\n // At most 32 iteration\r\n for (let i = 0; i < 32; i++) {\r\n if (interval < EPSILON_NUMERIC) {\r\n break;\r\n }\r\n prev = t - interval;\r\n next = t + interval;\r\n // t - interval\r\n _v1[0] = cubicAt(x0, x1, x2, x3, prev);\r\n _v1[1] = cubicAt(y0, y1, y2, y3, prev);\r\n\r\n d1 = v2DistSquare(_v1, _v0);\r\n\r\n if (prev >= 0 && d1 < d) {\r\n t = prev;\r\n d = d1;\r\n }\r\n else {\r\n // t + interval\r\n _v2[0] = cubicAt(x0, x1, x2, x3, next);\r\n _v2[1] = cubicAt(y0, y1, y2, y3, next);\r\n d2 = v2DistSquare(_v2, _v0);\r\n\r\n if (next <= 1 && d2 < d) {\r\n t = next;\r\n d = d2;\r\n }\r\n else {\r\n interval *= 0.5;\r\n }\r\n }\r\n }\r\n // t\r\n if (out) {\r\n out[0] = cubicAt(x0, x1, x2, x3, t);\r\n out[1] = cubicAt(y0, y1, y2, y3, t);\r\n }\r\n // console.log(interval, i);\r\n return mathSqrt(d);\r\n}\r\n\r\n/**\r\n * 计算三次贝塞尔曲线长度\r\n */\r\nexport function cubicLength(\r\n x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number,\r\n iteration: number\r\n) {\r\n let px = x0;\r\n let py = y0;\r\n\r\n let d = 0;\r\n\r\n const step = 1 / iteration;\r\n\r\n for (let i = 1; i <= iteration; i++) {\r\n let t = i * step;\r\n const x = cubicAt(x0, x1, x2, x3, t);\r\n const y = cubicAt(y0, y1, y2, y3, t);\r\n\r\n const dx = x - px;\r\n const dy = y - py;\r\n\r\n d += Math.sqrt(dx * dx + dy * dy);\r\n\r\n px = x;\r\n py = y;\r\n }\r\n\r\n return d;\r\n}\r\n\r\n/**\r\n * 计算二次方贝塞尔值\r\n */\r\nexport function quadraticAt(p0: number, p1: number, p2: number, t: number): number {\r\n const onet = 1 - t;\r\n return onet * (onet * p0 + 2 * t * p1) + t * t * p2;\r\n}\r\n\r\n/**\r\n * 计算二次方贝塞尔导数值\r\n */\r\nexport function quadraticDerivativeAt(p0: number, p1: number, p2: number, t: number): number {\r\n return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1));\r\n}\r\n\r\n/**\r\n * 计算二次方贝塞尔方程根\r\n * @return 有效根数目\r\n */\r\nexport function quadraticRootAt(p0: number, p1: number, p2: number, val: number, roots: number[]): number {\r\n const a = p0 - 2 * p1 + p2;\r\n const b = 2 * (p1 - p0);\r\n const c = p0 - val;\r\n\r\n let n = 0;\r\n if (isAroundZero(a)) {\r\n if (isNotAroundZero(b)) {\r\n const t1 = -c / b;\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n }\r\n }\r\n else {\r\n const disc = b * b - 4 * a * c;\r\n if (isAroundZero(disc)) {\r\n const t1 = -b / (2 * a);\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n }\r\n else if (disc > 0) {\r\n const discSqrt = mathSqrt(disc);\r\n const t1 = (-b + discSqrt) / (2 * a);\r\n const t2 = (-b - discSqrt) / (2 * a);\r\n if (t1 >= 0 && t1 <= 1) {\r\n roots[n++] = t1;\r\n }\r\n if (t2 >= 0 && t2 <= 1) {\r\n roots[n++] = t2;\r\n }\r\n }\r\n }\r\n return n;\r\n}\r\n\r\n/**\r\n * 计算二次贝塞尔方程极限值\r\n */\r\nexport function quadraticExtremum(p0: number, p1: number, p2: number): number {\r\n const divider = p0 + p2 - 2 * p1;\r\n if (divider === 0) {\r\n // p1 is center of p0 and p2\r\n return 0.5;\r\n }\r\n else {\r\n return (p0 - p1) / divider;\r\n }\r\n}\r\n\r\n/**\r\n * 细分二次贝塞尔曲线\r\n */\r\nexport function quadraticSubdivide(p0: number, p1: number, p2: number, t: number, out: number[]) {\r\n const p01 = (p1 - p0) * t + p0;\r\n const p12 = (p2 - p1) * t + p1;\r\n const p012 = (p12 - p01) * t + p01;\r\n\r\n // Seg0\r\n out[0] = p0;\r\n out[1] = p01;\r\n out[2] = p012;\r\n\r\n // Seg1\r\n out[3] = p012;\r\n out[4] = p12;\r\n out[5] = p2;\r\n}\r\n\r\n/**\r\n * 投射点到二次贝塞尔曲线上,返回投射距离。\r\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\r\n * @param {number} x0\r\n * @param {number} y0\r\n * @param {number} x1\r\n * @param {number} y1\r\n * @param {number} x2\r\n * @param {number} y2\r\n * @param {number} x\r\n * @param {number} y\r\n * @param {Array.} out 投射点\r\n * @return {number}\r\n */\r\nexport function quadraticProjectPoint(\r\n x0: number, y0: number, x1: number, y1: number, x2: number, y2: number,\r\n x: number, y: number, out: VectorArray\r\n): number {\r\n // http://pomax.github.io/bezierinfo/#projections\r\n let t: number;\r\n let interval = 0.005;\r\n let d = Infinity;\r\n\r\n _v0[0] = x;\r\n _v0[1] = y;\r\n\r\n // 先粗略估计一下可能的最小距离的 t 值\r\n // PENDING\r\n for (let _t = 0; _t < 1; _t += 0.05) {\r\n _v1[0] = quadraticAt(x0, x1, x2, _t);\r\n _v1[1] = quadraticAt(y0, y1, y2, _t);\r\n const d1 = v2DistSquare(_v0, _v1);\r\n if (d1 < d) {\r\n t = _t;\r\n d = d1;\r\n }\r\n }\r\n d = Infinity;\r\n\r\n // At most 32 iteration\r\n for (let i = 0; i < 32; i++) {\r\n if (interval < EPSILON_NUMERIC) {\r\n break;\r\n }\r\n const prev = t - interval;\r\n const next = t + interval;\r\n // t - interval\r\n _v1[0] = quadraticAt(x0, x1, x2, prev);\r\n _v1[1] = quadraticAt(y0, y1, y2, prev);\r\n\r\n const d1 = v2DistSquare(_v1, _v0);\r\n\r\n if (prev >= 0 && d1 < d) {\r\n t = prev;\r\n d = d1;\r\n }\r\n else {\r\n // t + interval\r\n _v2[0] = quadraticAt(x0, x1, x2, next);\r\n _v2[1] = quadraticAt(y0, y1, y2, next);\r\n const d2 = v2DistSquare(_v2, _v0);\r\n if (next <= 1 && d2 < d) {\r\n t = next;\r\n d = d2;\r\n }\r\n else {\r\n interval *= 0.5;\r\n }\r\n }\r\n }\r\n // t\r\n if (out) {\r\n out[0] = quadraticAt(x0, x1, x2, t);\r\n out[1] = quadraticAt(y0, y1, y2, t);\r\n }\r\n // console.log(interval, i);\r\n return mathSqrt(d);\r\n}\r\n\r\n/**\r\n * 计算二次贝塞尔曲线长度\r\n */\r\nexport function quadraticLength(\r\n x0: number, y0: number, x1: number, y1: number, x2: number, y2: number,\r\n iteration: number\r\n) {\r\n let px = x0;\r\n let py = y0;\r\n\r\n let d = 0;\r\n\r\n const step = 1 / iteration;\r\n\r\n for (let i = 1; i <= iteration; i++) {\r\n let t = i * step;\r\n const x = quadraticAt(x0, x1, x2, t);\r\n const y = quadraticAt(y0, y1, y2, t);\r\n\r\n const dx = x - px;\r\n const dy = y - py;\r\n\r\n d += Math.sqrt(dx * dx + dy * dy);\r\n\r\n px = x;\r\n py = y;\r\n }\r\n\r\n return d;\r\n}\r\n","import { cubicAt, cubicRootAt } from '../core/curve';\r\nimport { trim } from '../core/util';\r\n\r\nconst regexp = /cubic-bezier\\(([0-9,\\.e ]+)\\)/;\r\n\r\nexport function createCubicEasingFunc(cubicEasingStr: string) {\r\n const cubic = cubicEasingStr && regexp.exec(cubicEasingStr);\r\n if (cubic) {\r\n const points = cubic[1].split(',');\r\n const a = +trim(points[0]);\r\n const b = +trim(points[1]);\r\n const c = +trim(points[2]);\r\n const d = +trim(points[3]);\r\n\r\n if (isNaN(a + b + c + d)) {\r\n return;\r\n }\r\n\r\n const roots: number[] = [];\r\n return (p: number) => {\r\n return p <= 0\r\n ? 0 : p >= 1\r\n ? 1\r\n : cubicRootAt(0, a, c, 1, p, roots) && cubicAt(0, b, d, 1, roots[0]);\r\n };\r\n }\r\n}","/**\r\n * 动画主控制器\r\n * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件\r\n * @config life(1000) 动画时长\r\n * @config delay(0) 动画延迟时间\r\n * @config loop(true)\r\n * @config onframe\r\n * @config easing(optional)\r\n * @config ondestroy(optional)\r\n * @config onrestart(optional)\r\n *\r\n * TODO pause\r\n */\r\n\r\nimport easingFuncs, {AnimationEasing} from './easing';\r\nimport type Animation from './Animation';\r\nimport { isFunction, noop } from '../core/util';\r\nimport { createCubicEasingFunc } from './cubicEasing';\r\n\r\ntype OnframeCallback = (percent: number) => void;\r\ntype ondestroyCallback = () => void\r\ntype onrestartCallback = () => void\r\n\r\nexport type DeferredEventTypes = 'destroy' | 'restart'\r\n// type DeferredEventKeys = 'ondestroy' | 'onrestart'\r\n\r\nexport interface ClipProps {\r\n life?: number\r\n delay?: number\r\n loop?: boolean\r\n easing?: AnimationEasing\r\n\r\n onframe?: OnframeCallback\r\n ondestroy?: ondestroyCallback\r\n onrestart?: onrestartCallback\r\n}\r\n\r\nexport default class Clip {\r\n\r\n private _life: number\r\n private _delay: number\r\n\r\n private _inited: boolean = false\r\n private _startTime = 0 // 开始时间单位毫秒\r\n\r\n private _pausedTime = 0\r\n private _paused = false\r\n\r\n animation: Animation\r\n\r\n loop: boolean\r\n\r\n easing: AnimationEasing\r\n easingFunc: (p: number) => number\r\n\r\n // For linked list. Readonly\r\n next: Clip\r\n prev: Clip\r\n\r\n onframe: OnframeCallback\r\n ondestroy: ondestroyCallback\r\n onrestart: onrestartCallback\r\n\r\n constructor(opts: ClipProps) {\r\n\r\n this._life = opts.life || 1000;\r\n this._delay = opts.delay || 0;\r\n\r\n this.loop = opts.loop || false;\r\n\r\n this.onframe = opts.onframe || noop;\r\n this.ondestroy = opts.ondestroy || noop;\r\n this.onrestart = opts.onrestart || noop;\r\n\r\n opts.easing && this.setEasing(opts.easing);\r\n }\r\n\r\n step(globalTime: number, deltaTime: number): boolean {\r\n // Set startTime on first step, or _startTime may has milleseconds different between clips\r\n // PENDING\r\n if (!this._inited) {\r\n this._startTime = globalTime + this._delay;\r\n this._inited = true;\r\n }\r\n\r\n if (this._paused) {\r\n this._pausedTime += deltaTime;\r\n return;\r\n }\r\n\r\n const life = this._life;\r\n let elapsedTime = globalTime - this._startTime - this._pausedTime;\r\n let percent = elapsedTime / life;\r\n\r\n // PENDING: Not begin yet. Still run the loop.\r\n // In the case callback needs to be invoked.\r\n // Or want to update to the begin state at next frame when `setToFinal` and `delay` are both used.\r\n // To avoid the unexpected blink.\r\n if (percent < 0) {\r\n percent = 0;\r\n }\r\n\r\n percent = Math.min(percent, 1);\r\n\r\n const easingFunc = this.easingFunc;\r\n const schedule = easingFunc ? easingFunc(percent) : percent;\r\n\r\n this.onframe(schedule);\r\n\r\n // 结束\r\n if (percent === 1) {\r\n if (this.loop) {\r\n // Restart\r\n const remainder = elapsedTime % life;\r\n this._startTime = globalTime - remainder;\r\n this._pausedTime = 0;\r\n\r\n this.onrestart();\r\n }\r\n else {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n pause() {\r\n this._paused = true;\r\n }\r\n\r\n resume() {\r\n this._paused = false;\r\n }\r\n\r\n setEasing(easing: AnimationEasing) {\r\n this.easing = easing;\r\n this.easingFunc = isFunction(easing)\r\n ? easing\r\n : easingFuncs[easing] || createCubicEasingFunc(easing);\r\n }\r\n}","import { Dictionary } from './types';\r\n\r\n// Simple LRU cache use doubly linked list\r\n// @module zrender/core/LRU\r\n\r\nexport class Entry {\r\n\r\n value: T\r\n\r\n key: string | number\r\n\r\n next: Entry\r\n\r\n prev: Entry\r\n\r\n constructor(val: T) {\r\n this.value = val;\r\n }\r\n}\r\n/**\r\n * Simple double linked list. Compared with array, it has O(1) remove operation.\r\n * @constructor\r\n */\r\nexport class LinkedList {\r\n\r\n head: Entry\r\n tail: Entry\r\n\r\n private _len = 0\r\n\r\n /**\r\n * Insert a new value at the tail\r\n */\r\n insert(val: T): Entry {\r\n const entry = new Entry(val);\r\n this.insertEntry(entry);\r\n return entry;\r\n }\r\n\r\n /**\r\n * Insert an entry at the tail\r\n */\r\n insertEntry(entry: Entry) {\r\n if (!this.head) {\r\n this.head = this.tail = entry;\r\n }\r\n else {\r\n this.tail.next = entry;\r\n entry.prev = this.tail;\r\n entry.next = null;\r\n this.tail = entry;\r\n }\r\n this._len++;\r\n }\r\n\r\n /**\r\n * Remove entry.\r\n */\r\n remove(entry: Entry) {\r\n const prev = entry.prev;\r\n const next = entry.next;\r\n if (prev) {\r\n prev.next = next;\r\n }\r\n else {\r\n // Is head\r\n this.head = next;\r\n }\r\n if (next) {\r\n next.prev = prev;\r\n }\r\n else {\r\n // Is tail\r\n this.tail = prev;\r\n }\r\n entry.next = entry.prev = null;\r\n this._len--;\r\n }\r\n\r\n /**\r\n * Get length\r\n */\r\n len(): number {\r\n return this._len;\r\n }\r\n\r\n /**\r\n * Clear list\r\n */\r\n clear() {\r\n this.head = this.tail = null;\r\n this._len = 0;\r\n }\r\n\r\n}\r\n\r\n/**\r\n * LRU Cache\r\n */\r\nexport default class LRU {\r\n\r\n private _list = new LinkedList()\r\n\r\n private _maxSize = 10\r\n\r\n private _lastRemovedEntry: Entry\r\n\r\n private _map: Dictionary> = {}\r\n\r\n constructor(maxSize: number) {\r\n this._maxSize = maxSize;\r\n }\r\n\r\n /**\r\n * @return Removed value\r\n */\r\n put(key: string | number, value: T): T {\r\n const list = this._list;\r\n const map = this._map;\r\n let removed = null;\r\n if (map[key] == null) {\r\n const len = list.len();\r\n // Reuse last removed entry\r\n let entry = this._lastRemovedEntry;\r\n\r\n if (len >= this._maxSize && len > 0) {\r\n // Remove the least recently used\r\n const leastUsedEntry = list.head;\r\n list.remove(leastUsedEntry);\r\n delete map[leastUsedEntry.key];\r\n\r\n removed = leastUsedEntry.value;\r\n this._lastRemovedEntry = leastUsedEntry;\r\n }\r\n\r\n if (entry) {\r\n entry.value = value;\r\n }\r\n else {\r\n entry = new Entry(value);\r\n }\r\n entry.key = key;\r\n list.insertEntry(entry);\r\n map[key] = entry;\r\n }\r\n\r\n return removed;\r\n }\r\n\r\n get(key: string | number): T {\r\n const entry = this._map[key];\r\n const list = this._list;\r\n if (entry != null) {\r\n // Put the latest used entry in the tail\r\n if (entry !== list.tail) {\r\n list.remove(entry);\r\n list.insertEntry(entry);\r\n }\r\n\r\n return entry.value;\r\n }\r\n }\r\n\r\n /**\r\n * Clear the cache\r\n */\r\n clear() {\r\n this._list.clear();\r\n this._map = {};\r\n }\r\n\r\n len() {\r\n return this._list.len();\r\n }\r\n}","import LRU from '../core/LRU';\nimport { extend, isGradientObject, isString, map } from '../core/util';\nimport { GradientObject } from '../graphic/Gradient';\n\nconst kCSSColorTable = {\n 'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],\n 'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],\n 'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],\n 'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],\n 'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],\n 'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],\n 'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],\n 'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],\n 'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],\n 'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],\n 'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],\n 'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],\n 'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],\n 'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],\n 'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],\n 'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],\n 'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],\n 'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],\n 'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],\n 'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],\n 'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],\n 'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],\n 'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],\n 'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],\n 'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],\n 'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],\n 'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],\n 'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],\n 'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],\n 'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],\n 'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],\n 'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],\n 'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],\n 'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],\n 'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],\n 'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],\n 'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],\n 'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],\n 'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],\n 'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],\n 'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],\n 'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],\n 'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],\n 'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],\n 'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],\n 'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],\n 'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],\n 'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],\n 'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],\n 'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],\n 'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],\n 'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],\n 'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],\n 'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],\n 'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],\n 'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],\n 'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],\n 'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],\n 'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],\n 'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],\n 'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],\n 'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],\n 'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],\n 'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],\n 'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],\n 'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],\n 'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],\n 'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],\n 'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],\n 'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],\n 'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],\n 'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],\n 'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],\n 'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]\n};\n\nfunction clampCssByte(i: number): number { // Clamp to integer 0 .. 255.\n i = Math.round(i); // Seems to be what Chrome does (vs truncation).\n return i < 0 ? 0 : i > 255 ? 255 : i;\n}\n\nfunction clampCssAngle(i: number): number { // Clamp to integer 0 .. 360.\n i = Math.round(i); // Seems to be what Chrome does (vs truncation).\n return i < 0 ? 0 : i > 360 ? 360 : i;\n}\n\nfunction clampCssFloat(f: number): number { // Clamp to float 0.0 .. 1.0.\n return f < 0 ? 0 : f > 1 ? 1 : f;\n}\n\nfunction parseCssInt(val: string | number): number { // int or percentage.\n let str = val as string;\n if (str.length && str.charAt(str.length - 1) === '%') {\n return clampCssByte(parseFloat(str) / 100 * 255);\n }\n return clampCssByte(parseInt(str, 10));\n}\n\nfunction parseCssFloat(val: string | number): number { // float or percentage.\n let str = val as string;\n if (str.length && str.charAt(str.length - 1) === '%') {\n return clampCssFloat(parseFloat(str) / 100);\n }\n return clampCssFloat(parseFloat(str));\n}\n\nfunction cssHueToRgb(m1: number, m2: number, h: number): number {\n if (h < 0) {\n h += 1;\n }\n else if (h > 1) {\n h -= 1;\n }\n\n if (h * 6 < 1) {\n return m1 + (m2 - m1) * h * 6;\n }\n if (h * 2 < 1) {\n return m2;\n }\n if (h * 3 < 2) {\n return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n }\n return m1;\n}\n\nfunction lerpNumber(a: number, b: number, p: number): number {\n return a + (b - a) * p;\n}\n\nfunction setRgba(out: number[], r: number, g: number, b: number, a: number): number[] {\n out[0] = r;\n out[1] = g;\n out[2] = b;\n out[3] = a;\n return out;\n}\nfunction copyRgba(out: number[], a: number[]) {\n out[0] = a[0];\n out[1] = a[1];\n out[2] = a[2];\n out[3] = a[3];\n return out;\n}\n\nconst colorCache = new LRU(20);\nlet lastRemovedArr: number[] = null;\n\nfunction putToCache(colorStr: string, rgbaArr: number[]) {\n // Reuse removed array\n if (lastRemovedArr) {\n copyRgba(lastRemovedArr, rgbaArr);\n }\n lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));\n}\n\nexport function parse(colorStr: string, rgbaArr?: number[]): number[] {\n if (!colorStr) {\n return;\n }\n rgbaArr = rgbaArr || [];\n\n let cached = colorCache.get(colorStr);\n if (cached) {\n return copyRgba(rgbaArr, cached);\n }\n\n // colorStr may be not string\n colorStr = colorStr + '';\n // Remove all whitespace, not compliant, but should just be more accepting.\n let str = colorStr.replace(/ /g, '').toLowerCase();\n\n // Color keywords (and transparent) lookup.\n if (str in kCSSColorTable) {\n copyRgba(rgbaArr, kCSSColorTable[str as keyof typeof kCSSColorTable]);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n\n // supports the forms #rgb, #rrggbb, #rgba, #rrggbbaa\n // #rrggbbaa(use the last pair of digits as alpha)\n // see https://drafts.csswg.org/css-color/#hex-notation\n const strLen = str.length;\n if (str.charAt(0) === '#') {\n if (strLen === 4 || strLen === 5) {\n const iv = parseInt(str.slice(1, 4), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xfff)) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return; // Covers NaN.\n }\n // interpret values of the form #rgb as #rrggbb and #rgba as #rrggbbaa\n setRgba(rgbaArr,\n ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),\n (iv & 0xf0) | ((iv & 0xf0) >> 4),\n (iv & 0xf) | ((iv & 0xf) << 4),\n strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n else if (strLen === 7 || strLen === 9) {\n const iv = parseInt(str.slice(1, 7), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xffffff)) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return; // Covers NaN.\n }\n setRgba(rgbaArr,\n (iv & 0xff0000) >> 16,\n (iv & 0xff00) >> 8,\n iv & 0xff,\n strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n\n return;\n }\n let op = str.indexOf('(');\n let ep = str.indexOf(')');\n if (op !== -1 && ep + 1 === strLen) {\n let fname = str.substr(0, op);\n let params: (number | string)[] = str.substr(op + 1, ep - (op + 1)).split(',');\n let alpha = 1; // To allow case fallthrough.\n switch (fname) {\n case 'rgba':\n if (params.length !== 4) {\n return params.length === 3\n // to be compatible with rgb\n ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)\n : setRgba(rgbaArr, 0, 0, 0, 1);\n }\n alpha = parseCssFloat(params.pop()); // jshint ignore:line\n // Fall through.\n case 'rgb':\n if (params.length >= 3) {\n setRgba(rgbaArr,\n parseCssInt(params[0]),\n parseCssInt(params[1]),\n parseCssInt(params[2]),\n params.length === 3 ? alpha : parseCssFloat(params[3])\n );\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n }\n else {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n case 'hsla':\n if (params.length !== 4) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n params[3] = parseCssFloat(params[3]);\n hsla2rgba(params, rgbaArr);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n case 'hsl':\n if (params.length !== 3) {\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n }\n hsla2rgba(params, rgbaArr);\n putToCache(colorStr, rgbaArr);\n return rgbaArr;\n default:\n return;\n }\n }\n\n setRgba(rgbaArr, 0, 0, 0, 1);\n return;\n}\n\nfunction hsla2rgba(hsla: (number | string) [], rgba?: number[]): number[] {\n const h = (((parseFloat(hsla[0] as string) % 360) + 360) % 360) / 360; // 0 .. 1\n // NOTE(deanm): According to the CSS spec s/l should only be\n // percentages, but we don't bother and let float or percentage.\n const s = parseCssFloat(hsla[1]);\n const l = parseCssFloat(hsla[2]);\n const m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n const m1 = l * 2 - m2;\n\n rgba = rgba || [];\n setRgba(rgba,\n clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),\n clampCssByte(cssHueToRgb(m1, m2, h) * 255),\n clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),\n 1\n );\n\n if (hsla.length === 4) {\n rgba[3] = hsla[3] as number;\n }\n\n return rgba;\n}\n\nfunction rgba2hsla(rgba: number[]): number[] {\n if (!rgba) {\n return;\n }\n\n // RGB from 0 to 255\n const R = rgba[0] / 255;\n const G = rgba[1] / 255;\n const B = rgba[2] / 255;\n\n const vMin = Math.min(R, G, B); // Min. value of RGB\n const vMax = Math.max(R, G, B); // Max. value of RGB\n const delta = vMax - vMin; // Delta RGB value\n\n const L = (vMax + vMin) / 2;\n let H;\n let S;\n // HSL results from 0 to 1\n if (delta === 0) {\n H = 0;\n S = 0;\n }\n else {\n if (L < 0.5) {\n S = delta / (vMax + vMin);\n }\n else {\n S = delta / (2 - vMax - vMin);\n }\n\n const deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;\n const deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;\n const deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;\n\n if (R === vMax) {\n H = deltaB - deltaG;\n }\n else if (G === vMax) {\n H = (1 / 3) + deltaR - deltaB;\n }\n else if (B === vMax) {\n H = (2 / 3) + deltaG - deltaR;\n }\n\n if (H < 0) {\n H += 1;\n }\n\n if (H > 1) {\n H -= 1;\n }\n }\n\n const hsla = [H * 360, S, L];\n\n if (rgba[3] != null) {\n hsla.push(rgba[3]);\n }\n\n return hsla;\n}\n\nexport function lift(color: string, level: number) {\n const colorArr = parse(color);\n if (colorArr) {\n for (let i = 0; i < 3; i++) {\n if (level < 0) {\n colorArr[i] = colorArr[i] * (1 - level) | 0;\n }\n else {\n colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;\n }\n if (colorArr[i] > 255) {\n colorArr[i] = 255;\n }\n else if (colorArr[i] < 0) {\n colorArr[i] = 0;\n }\n }\n return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');\n }\n}\n\nexport function toHex(color: string): string {\n const colorArr = parse(color);\n if (colorArr) {\n return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);\n }\n}\n\n/**\n * Map value to color. Faster than lerp methods because color is represented by rgba array.\n * @param normalizedValue A float between 0 and 1.\n * @param colors List of rgba color array\n * @param out Mapped gba color array\n * @return will be null/undefined if input illegal.\n */\nexport function fastLerp(\n normalizedValue: number,\n colors: number[][],\n out?: number[]\n): number[] {\n if (!(colors && colors.length)\n || !(normalizedValue >= 0 && normalizedValue <= 1)\n ) {\n return;\n }\n\n out = out || [];\n\n const value = normalizedValue * (colors.length - 1);\n const leftIndex = Math.floor(value);\n const rightIndex = Math.ceil(value);\n const leftColor = colors[leftIndex];\n const rightColor = colors[rightIndex];\n const dv = value - leftIndex;\n out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));\n out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));\n out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));\n out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));\n\n return out;\n}\n\n/**\n * @deprecated\n */\nexport const fastMapToColor = fastLerp;\n\ntype LerpFullOutput = {\n color: string\n leftIndex: number\n rightIndex: number\n value: number\n}\n/**\n * @param normalizedValue A float between 0 and 1.\n * @param colors Color list.\n * @param fullOutput Default false.\n * @return Result color. If fullOutput,\n return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},\n */\nexport function lerp(\n normalizedValue: number,\n colors: string[],\n fullOutput: boolean\n): LerpFullOutput\nexport function lerp(\n normalizedValue: number,\n colors: string[]\n): string\nexport function lerp(\n normalizedValue: number,\n colors: string[],\n fullOutput?: boolean\n): string | LerpFullOutput {\n if (!(colors && colors.length)\n || !(normalizedValue >= 0 && normalizedValue <= 1)\n ) {\n return;\n }\n\n const value = normalizedValue * (colors.length - 1);\n const leftIndex = Math.floor(value);\n const rightIndex = Math.ceil(value);\n const leftColor = parse(colors[leftIndex]);\n const rightColor = parse(colors[rightIndex]);\n const dv = value - leftIndex;\n\n const color = stringify(\n [\n clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),\n clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),\n clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),\n clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))\n ],\n 'rgba'\n );\n\n return fullOutput\n ? {\n color: color,\n leftIndex: leftIndex,\n rightIndex: rightIndex,\n value: value\n }\n : color;\n}\n\n/**\n * @deprecated\n */\nexport const mapToColor = lerp;\n\n/**\n * @param color\n * @param h 0 ~ 360, ignore when null.\n * @param s 0 ~ 1, ignore when null.\n * @param l 0 ~ 1, ignore when null.\n * @return Color string in rgba format.\n * @memberOf module:zrender/util/color\n */\nexport function modifyHSL(color: string, h?: number, s?: number, l?: number): string {\n let colorArr = parse(color);\n\n if (color) {\n colorArr = rgba2hsla(colorArr);\n h != null && (colorArr[0] = clampCssAngle(h));\n s != null && (colorArr[1] = parseCssFloat(s));\n l != null && (colorArr[2] = parseCssFloat(l));\n\n return stringify(hsla2rgba(colorArr), 'rgba');\n }\n}\n\n/**\n * @param color\n * @param alpha 0 ~ 1\n * @return Color string in rgba format.\n * @memberOf module:zrender/util/color\n */\nexport function modifyAlpha(color: string, alpha?: number): string {\n const colorArr = parse(color);\n\n if (colorArr && alpha != null) {\n colorArr[3] = clampCssFloat(alpha);\n return stringify(colorArr, 'rgba');\n }\n}\n\n/**\n * @param arrColor like [12,33,44,0.4]\n * @param type 'rgba', 'hsva', ...\n * @return Result color. (If input illegal, return undefined).\n */\nexport function stringify(arrColor: number[], type: string): string {\n if (!arrColor || !arrColor.length) {\n return;\n }\n let colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];\n if (type === 'rgba' || type === 'hsva' || type === 'hsla') {\n colorStr += ',' + arrColor[3];\n }\n return type + '(' + colorStr + ')';\n}\n\n/**\n * Calculate luminance. It will include alpha.\n */\nexport function lum(color: string, backgroundLum: number) {\n const arr = parse(color);\n return arr\n ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255\n + (1 - arr[3]) * backgroundLum // Blending with assumed white background.\n : 0;\n}\n\n/**\n * Generate a random color\n */\nexport function random(): string {\n return stringify([\n Math.round(Math.random() * 255),\n Math.round(Math.random() * 255),\n Math.round(Math.random() * 255)\n ], 'rgb');\n}\n\nconst liftedColorCache = new LRU(100);\nexport function liftColor(color: GradientObject): GradientObject;\nexport function liftColor(color: string): string;\nexport function liftColor(color: string | GradientObject): string | GradientObject {\n if (isString(color)) {\n let liftedColor = liftedColorCache.get(color);\n if (!liftedColor) {\n liftedColor = lift(color, -0.1);\n liftedColorCache.put(color, liftedColor);\n }\n return liftedColor;\n }\n else if (isGradientObject(color)) {\n const ret = extend({}, color) as GradientObject;\n ret.colorStops = map(color.colorStops, stop => ({\n offset: stop.offset,\n color: lift(stop.color, -0.1)\n }));\n return ret;\n }\n // Change nothing.\n return color;\n}\n","// Shared methods of svg and svg-ssr\r\n\r\nimport { MatrixArray } from '../core/matrix';\r\nimport Transformable, { TransformProp } from '../core/Transformable';\r\nimport { RADIAN_TO_DEGREE, retrieve2, logError, isFunction } from '../core/util';\r\nimport Displayable from '../graphic/Displayable';\r\nimport { GradientObject } from '../graphic/Gradient';\r\nimport { LinearGradientObject } from '../graphic/LinearGradient';\r\nimport Path from '../graphic/Path';\r\nimport { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern';\r\nimport { RadialGradientObject } from '../graphic/RadialGradient';\r\nimport { parse } from '../tool/color';\r\nimport env from '../core/env';\r\n\r\nconst mathRound = Math.round;\r\n\r\nexport function normalizeColor(color: string): { color: string; opacity: number; } {\r\n let opacity;\r\n if (!color || color === 'transparent') {\r\n color = 'none';\r\n }\r\n else if (typeof color === 'string' && color.indexOf('rgba') > -1) {\r\n const arr = parse(color);\r\n if (arr) {\r\n // TODO use hex?\r\n color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')';\r\n opacity = arr[3];\r\n }\r\n }\r\n return {\r\n color,\r\n opacity: opacity == null ? 1 : opacity\r\n };\r\n}\r\nconst EPSILON = 1e-4;\r\nexport function isAroundZero(transform: number) {\r\n return transform < EPSILON && transform > -EPSILON;\r\n}\r\n\r\nexport function round3(transform: number) {\r\n return mathRound(transform * 1e3) / 1e3;\r\n}\r\nexport function round4(transform: number) {\r\n return mathRound(transform * 1e4) / 1e4;\r\n}\r\nexport function round1(transform: number) {\r\n return mathRound(transform * 10) / 10;\r\n}\r\n\r\nexport function getMatrixStr(m: MatrixArray) {\r\n return 'matrix('\r\n // Avoid large string of matrix\r\n // PENDING If have precision issue when scaled\r\n + round3(m[0]) + ','\r\n + round3(m[1]) + ','\r\n + round3(m[2]) + ','\r\n + round3(m[3]) + ','\r\n + round4(m[4]) + ','\r\n + round4(m[5])\r\n + ')';\r\n}\r\n\r\nexport const TEXT_ALIGN_TO_ANCHOR = {\r\n left: 'start',\r\n right: 'end',\r\n center: 'middle',\r\n middle: 'middle'\r\n};\r\n\r\nexport function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number {\r\n // TODO Other baselines.\r\n if (textBaseline === 'top') {\r\n y += lineHeight / 2;\r\n }\r\n else if (textBaseline === 'bottom') {\r\n y -= lineHeight / 2;\r\n }\r\n return y;\r\n}\r\n\r\n\r\nexport function hasShadow(style: Displayable['style']) {\r\n // TODO: textBoxShadowBlur is not supported yet\r\n return style\r\n && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY);\r\n}\r\n\r\nexport function getShadowKey(displayable: Displayable) {\r\n const style = displayable.style;\r\n const globalScale = displayable.getGlobalScale();\r\n return [\r\n style.shadowColor,\r\n (style.shadowBlur || 0).toFixed(2), // Reduce the precision\r\n (style.shadowOffsetX || 0).toFixed(2),\r\n (style.shadowOffsetY || 0).toFixed(2),\r\n globalScale[0],\r\n globalScale[1]\r\n ].join(',');\r\n}\r\n\r\nexport function getClipPathsKey(clipPaths: Path[]) {\r\n let key: number[] = [];\r\n if (clipPaths) {\r\n for (let i = 0; i < clipPaths.length; i++) {\r\n const clipPath = clipPaths[i];\r\n key.push(clipPath.id);\r\n }\r\n }\r\n return key.join(',');\r\n}\r\n\r\nexport function isImagePattern(val: any): val is ImagePatternObject {\r\n return val && (!!(val as ImagePatternObject).image);\r\n}\r\nexport function isSVGPattern(val: any): val is SVGPatternObject {\r\n return val && (!!(val as SVGPatternObject).svgElement);\r\n}\r\nexport function isPattern(val: any): val is PatternObject {\r\n return isImagePattern(val) || isSVGPattern(val);\r\n}\r\n\r\nexport function isLinearGradient(val: GradientObject): val is LinearGradientObject {\r\n return val.type === 'linear';\r\n}\r\n\r\nexport function isRadialGradient(val: GradientObject): val is RadialGradientObject {\r\n return val.type === 'radial';\r\n}\r\n\r\nexport function isGradient(val: any): val is GradientObject {\r\n return val && (\r\n (val as GradientObject).type === 'linear'\r\n || (val as GradientObject).type === 'radial'\r\n );\r\n}\r\n\r\nexport function getIdURL(id: string) {\r\n return `url(#${id})`;\r\n}\r\n\r\nexport function getPathPrecision(el: Path) {\r\n const scale = el.getGlobalScale();\r\n const size = Math.max(scale[0], scale[1]);\r\n return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1);\r\n}\r\n\r\nexport function getSRTTransformString(\r\n transform: Partial>\r\n) {\r\n const x = transform.x || 0;\r\n const y = transform.y || 0;\r\n const rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE;\r\n const scaleX = retrieve2(transform.scaleX, 1);\r\n const scaleY = retrieve2(transform.scaleY, 1);\r\n const skewX = transform.skewX || 0;\r\n const skewY = transform.skewY || 0;\r\n const res = [];\r\n if (x || y) {\r\n // TODO not using px unit?\r\n res.push(`translate(${x}px,${y}px)`);\r\n }\r\n if (rotation) {\r\n res.push(`rotate(${rotation})`);\r\n }\r\n if (scaleX !== 1 || scaleY !== 1) {\r\n res.push(`scale(${scaleX},${scaleY})`);\r\n }\r\n if (skewX || skewY) {\r\n res.push(`skew(${mathRound(skewX * RADIAN_TO_DEGREE)}deg, ${mathRound(skewY * RADIAN_TO_DEGREE)}deg)`);\r\n }\r\n\r\n return res.join(' ');\r\n}\r\n\r\nexport const encodeBase64 = (function () {\r\n if (env.hasGlobalWindow && isFunction(window.btoa)) {\r\n return function (str: string) {\r\n return window.btoa(unescape(encodeURIComponent(str)));\r\n };\r\n }\r\n if (typeof Buffer !== 'undefined') {\r\n return function (str: string) {\r\n return Buffer.from(str).toString('base64');\r\n };\r\n }\r\n return function (str: string): string {\r\n if (process.env.NODE_ENV !== 'production') {\r\n logError('Base64 isn\\'t natively supported in the current environment.');\r\n }\r\n return null;\r\n };\r\n})();","/**\r\n * @module echarts/animation/Animator\r\n */\r\n\r\nimport Clip from './Clip';\r\nimport * as color from '../tool/color';\r\nimport {\r\n eqNaN,\r\n extend,\r\n isArrayLike,\r\n isFunction,\r\n isGradientObject,\r\n isNumber,\r\n isString,\r\n keys,\r\n logError,\r\n map\r\n} from '../core/util';\r\nimport {ArrayLike, Dictionary} from '../core/types';\r\nimport easingFuncs, { AnimationEasing } from './easing';\r\nimport Animation from './Animation';\r\nimport { createCubicEasingFunc } from './cubicEasing';\r\nimport { isLinearGradient, isRadialGradient } from '../svg/helper';\r\n\r\ntype NumberArray = ArrayLike\r\ntype InterpolatableType = string | number | NumberArray | NumberArray[];\r\n\r\ninterface ParsedColorStop {\r\n color: number[],\r\n offset: number\r\n};\r\n\r\ninterface ParsedGradientObject {\r\n colorStops: ParsedColorStop[]\r\n x: number\r\n y: number\r\n global: boolean\r\n}\r\ninterface ParsedLinearGradientObject extends ParsedGradientObject {\r\n x2: number\r\n y2: number\r\n}\r\ninterface ParsedRadialGradientObject extends ParsedGradientObject {\r\n r: number\r\n}\r\n\r\nconst arraySlice = Array.prototype.slice;\r\n\r\nfunction interpolateNumber(p0: number, p1: number, percent: number): number {\r\n return (p1 - p0) * percent + p0;\r\n}\r\nfunction interpolate1DArray(\r\n out: NumberArray,\r\n p0: NumberArray,\r\n p1: NumberArray,\r\n percent: number\r\n) {\r\n // TODO Handling different length TypedArray\r\n const len = p0.length;\r\n for (let i = 0; i < len; i++) {\r\n out[i] = interpolateNumber(p0[i], p1[i], percent);\r\n }\r\n return out;\r\n}\r\n\r\nfunction interpolate2DArray(\r\n out: NumberArray[],\r\n p0: NumberArray[],\r\n p1: NumberArray[],\r\n percent: number\r\n) {\r\n const len = p0.length;\r\n // TODO differnt length on each item?\r\n const len2 = len && p0[0].length;\r\n for (let i = 0; i < len; i++) {\r\n if (!out[i]) {\r\n out[i] = [];\r\n }\r\n for (let j = 0; j < len2; j++) {\r\n out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent);\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nfunction add1DArray(\r\n out: NumberArray,\r\n p0: NumberArray,\r\n p1: NumberArray,\r\n sign: 1 | -1\r\n) {\r\n const len = p0.length;\r\n for (let i = 0; i < len; i++) {\r\n out[i] = p0[i] + p1[i] * sign;\r\n }\r\n return out;\r\n}\r\n\r\nfunction add2DArray(\r\n out: NumberArray[],\r\n p0: NumberArray[],\r\n p1: NumberArray[],\r\n sign: 1 | -1\r\n) {\r\n const len = p0.length;\r\n const len2 = len && p0[0].length;\r\n for (let i = 0; i < len; i++) {\r\n if (!out[i]) {\r\n out[i] = [];\r\n }\r\n for (let j = 0; j < len2; j++) {\r\n out[i][j] = p0[i][j] + p1[i][j] * sign;\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nfunction fillColorStops(val0: ParsedColorStop[], val1: ParsedColorStop[]) {\r\n const len0 = val0.length;\r\n const len1 = val1.length;\r\n\r\n const shorterArr = len0 > len1 ? val1 : val0;\r\n const shorterLen = Math.min(len0, len1);\r\n const last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 };\r\n for (let i = shorterLen; i < Math.max(len0, len1); i++) {\r\n // Use last color stop to fill the shorter array\r\n shorterArr.push({\r\n offset: last.offset,\r\n color: last.color.slice()\r\n });\r\n }\r\n}\r\n// arr0 is source array, arr1 is target array.\r\n// Do some preprocess to avoid error happened when interpolating from arr0 to arr1\r\nfunction fillArray(\r\n val0: NumberArray | NumberArray[],\r\n val1: NumberArray | NumberArray[],\r\n arrDim: 1 | 2\r\n) {\r\n // TODO Handling different length TypedArray\r\n let arr0 = val0 as (number | number[])[];\r\n let arr1 = val1 as (number | number[])[];\r\n if (!arr0.push || !arr1.push) {\r\n return;\r\n }\r\n const arr0Len = arr0.length;\r\n const arr1Len = arr1.length;\r\n if (arr0Len !== arr1Len) {\r\n // FIXME Not work for TypedArray\r\n const isPreviousLarger = arr0Len > arr1Len;\r\n if (isPreviousLarger) {\r\n // Cut the previous\r\n arr0.length = arr1Len;\r\n }\r\n else {\r\n // Fill the previous\r\n for (let i = arr0Len; i < arr1Len; i++) {\r\n arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]));\r\n }\r\n }\r\n }\r\n // Handling NaN value\r\n const len2 = arr0[0] && (arr0[0] as number[]).length;\r\n for (let i = 0; i < arr0.length; i++) {\r\n if (arrDim === 1) {\r\n if (isNaN(arr0[i] as number)) {\r\n arr0[i] = arr1[i];\r\n }\r\n }\r\n else {\r\n for (let j = 0; j < len2; j++) {\r\n if (isNaN((arr0 as number[][])[i][j])) {\r\n (arr0 as number[][])[i][j] = (arr1 as number[][])[i][j];\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport function cloneValue(value: InterpolatableType) {\r\n if (isArrayLike(value)) {\r\n const len = value.length;\r\n if (isArrayLike(value[0])) {\r\n const ret = [];\r\n for (let i = 0; i < len; i++) {\r\n ret.push(arraySlice.call(value[i]));\r\n }\r\n return ret;\r\n }\r\n\r\n return arraySlice.call(value);\r\n }\r\n\r\n return value;\r\n}\r\n\r\nfunction rgba2String(rgba: number[]): string {\r\n rgba[0] = Math.floor(rgba[0]) || 0;\r\n rgba[1] = Math.floor(rgba[1]) || 0;\r\n rgba[2] = Math.floor(rgba[2]) || 0;\r\n rgba[3] = rgba[3] == null ? 1 : rgba[3];\r\n\r\n return 'rgba(' + rgba.join(',') + ')';\r\n}\r\n\r\nfunction guessArrayDim(value: ArrayLike): 1 | 2 {\r\n return isArrayLike(value && (value as ArrayLike)[0]) ? 2 : 1;\r\n}\r\n\r\nconst VALUE_TYPE_NUMBER = 0;\r\nconst VALUE_TYPE_1D_ARRAY = 1;\r\nconst VALUE_TYPE_2D_ARRAY = 2;\r\nconst VALUE_TYPE_COLOR = 3;\r\nconst VALUE_TYPE_LINEAR_GRADIENT = 4;\r\nconst VALUE_TYPE_RADIAL_GRADIENT = 5;\r\n// Other value type that can only use discrete animation.\r\nconst VALUE_TYPE_UNKOWN = 6;\r\n\r\ntype ValueType = 0 | 1 | 2 | 3 | 4 | 5 | 6;\r\n\r\ntype Keyframe = {\r\n time: number\r\n value: unknown\r\n percent: number\r\n // Raw value for discrete animation.\r\n rawValue: unknown\r\n\r\n easing?: AnimationEasing // Raw easing\r\n easingFunc?: (percent: number) => number\r\n additiveValue?: unknown\r\n}\r\n\r\n\r\nfunction isGradientValueType(valType: ValueType): valType is 4 | 5 {\r\n return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT;\r\n}\r\nfunction isArrayValueType(valType: ValueType): valType is 1 | 2 {\r\n return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY;\r\n}\r\n\r\n\r\nlet tmpRgba: number[] = [0, 0, 0, 0];\r\n\r\nclass Track {\r\n\r\n keyframes: Keyframe[] = []\r\n\r\n propName: string\r\n\r\n valType: ValueType\r\n\r\n discrete: boolean = false\r\n\r\n _invalid: boolean = false;\r\n\r\n private _finished: boolean\r\n\r\n private _needsSort: boolean = false\r\n\r\n private _additiveTrack: Track\r\n // Temporal storage for interpolated additive value.\r\n private _additiveValue: unknown\r\n\r\n // Info for run\r\n /**\r\n * Last frame\r\n */\r\n private _lastFr = 0\r\n /**\r\n * Percent of last frame.\r\n */\r\n private _lastFrP = 0\r\n\r\n constructor(propName: string) {\r\n this.propName = propName;\r\n }\r\n\r\n isFinished() {\r\n return this._finished;\r\n }\r\n\r\n setFinished() {\r\n this._finished = true;\r\n // Also set additive track to finished.\r\n // Make sure the final value stopped on the latest track\r\n if (this._additiveTrack) {\r\n this._additiveTrack.setFinished();\r\n }\r\n }\r\n\r\n needsAnimate() {\r\n return this.keyframes.length >= 1;\r\n }\r\n\r\n getAdditiveTrack() {\r\n return this._additiveTrack;\r\n }\r\n\r\n addKeyframe(time: number, rawValue: unknown, easing?: AnimationEasing) {\r\n this._needsSort = true;\r\n\r\n let keyframes = this.keyframes;\r\n let len = keyframes.length;\r\n\r\n let discrete = false;\r\n let valType: ValueType = VALUE_TYPE_UNKOWN;\r\n let value = rawValue;\r\n\r\n // Handling values only if it's possible to be interpolated.\r\n if (isArrayLike(rawValue)) {\r\n let arrayDim = guessArrayDim(rawValue);\r\n valType = arrayDim;\r\n // Not a number array.\r\n if (arrayDim === 1 && !isNumber(rawValue[0])\r\n || arrayDim === 2 && !isNumber(rawValue[0][0])) {\r\n discrete = true;\r\n }\r\n }\r\n else {\r\n if (isNumber(rawValue) && !eqNaN(rawValue)) {\r\n valType = VALUE_TYPE_NUMBER;\r\n }\r\n else if (isString(rawValue)) {\r\n if (!isNaN(+rawValue)) { // Can be string number like '2'\r\n valType = VALUE_TYPE_NUMBER;\r\n }\r\n else {\r\n const colorArray = color.parse(rawValue);\r\n if (colorArray) {\r\n value = colorArray;\r\n valType = VALUE_TYPE_COLOR;\r\n }\r\n }\r\n }\r\n else if (isGradientObject(rawValue)) {\r\n // TODO Color to gradient or gradient to color.\r\n const parsedGradient = extend({}, value) as unknown as ParsedGradientObject;\r\n parsedGradient.colorStops = map(rawValue.colorStops, colorStop => ({\r\n offset: colorStop.offset,\r\n color: color.parse(colorStop.color)\r\n }));\r\n if (isLinearGradient(rawValue)) {\r\n valType = VALUE_TYPE_LINEAR_GRADIENT;\r\n }\r\n else if (isRadialGradient(rawValue)) {\r\n valType = VALUE_TYPE_RADIAL_GRADIENT;\r\n }\r\n value = parsedGradient;\r\n }\r\n }\r\n\r\n if (len === 0) {\r\n // Inference type from the first keyframe.\r\n this.valType = valType;\r\n }\r\n // Not same value type or can't be interpolated.\r\n else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) {\r\n discrete = true;\r\n }\r\n\r\n this.discrete = this.discrete || discrete;\r\n\r\n const kf: Keyframe = {\r\n time,\r\n value,\r\n rawValue,\r\n percent: 0\r\n };\r\n if (easing) {\r\n // Save the raw easing name to be used in css animation output\r\n kf.easing = easing;\r\n kf.easingFunc = isFunction(easing)\r\n ? easing\r\n : easingFuncs[easing] || createCubicEasingFunc(easing);\r\n }\r\n // Not check if value equal here.\r\n keyframes.push(kf);\r\n return kf;\r\n }\r\n\r\n prepare(maxTime: number, additiveTrack?: Track) {\r\n let kfs = this.keyframes;\r\n if (this._needsSort) {\r\n // Sort keyframe as ascending\r\n kfs.sort(function (a: Keyframe, b: Keyframe) {\r\n return a.time - b.time;\r\n });\r\n }\r\n\r\n const valType = this.valType;\r\n const kfsLen = kfs.length;\r\n const lastKf = kfs[kfsLen - 1];\r\n const isDiscrete = this.discrete;\r\n const isArr = isArrayValueType(valType);\r\n const isGradient = isGradientValueType(valType);\r\n\r\n for (let i = 0; i < kfsLen; i++) {\r\n const kf = kfs[i];\r\n const value = kf.value;\r\n const lastValue = lastKf.value;\r\n kf.percent = kf.time / maxTime;\r\n if (!isDiscrete) {\r\n if (isArr && i !== kfsLen - 1) {\r\n // Align array with target frame.\r\n fillArray(value as NumberArray, lastValue as NumberArray, valType);\r\n }\r\n else if (isGradient) {\r\n fillColorStops(\r\n (value as ParsedLinearGradientObject).colorStops,\r\n (lastValue as ParsedLinearGradientObject).colorStops\r\n );\r\n }\r\n }\r\n }\r\n\r\n // Only apply additive animaiton on INTERPOLABLE SAME TYPE values.\r\n if (\r\n !isDiscrete\r\n // TODO support gradient\r\n && valType !== VALUE_TYPE_RADIAL_GRADIENT\r\n && additiveTrack\r\n // If two track both will be animated and have same value format.\r\n && this.needsAnimate()\r\n && additiveTrack.needsAnimate()\r\n && valType === additiveTrack.valType\r\n && !additiveTrack._finished\r\n ) {\r\n this._additiveTrack = additiveTrack;\r\n\r\n const startValue = kfs[0].value;\r\n // Calculate difference\r\n for (let i = 0; i < kfsLen; i++) {\r\n if (valType === VALUE_TYPE_NUMBER) {\r\n kfs[i].additiveValue = kfs[i].value as number - (startValue as number);\r\n }\r\n else if (valType === VALUE_TYPE_COLOR) {\r\n kfs[i].additiveValue =\r\n add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1);\r\n }\r\n else if (isArrayValueType(valType)) {\r\n kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY\r\n ? add1DArray([], kfs[i].value as NumberArray, startValue as NumberArray, -1)\r\n : add2DArray([], kfs[i].value as NumberArray[], startValue as NumberArray[], -1);\r\n }\r\n }\r\n }\r\n }\r\n\r\n step(target: any, percent: number) {\r\n if (this._finished) { // Track may be set to finished.\r\n return;\r\n }\r\n\r\n if (this._additiveTrack && this._additiveTrack._finished) {\r\n // Remove additive track if it's finished.\r\n this._additiveTrack = null;\r\n }\r\n const isAdditive = this._additiveTrack != null;\r\n const valueKey = isAdditive ? 'additiveValue' : 'value';\r\n\r\n const valType = this.valType;\r\n const keyframes = this.keyframes;\r\n const kfsNum = keyframes.length;\r\n const propName = this.propName;\r\n const isValueColor = valType === VALUE_TYPE_COLOR;\r\n // Find the range keyframes\r\n // kf1-----kf2---------current--------kf3\r\n // find kf2 and kf3 and do interpolation\r\n let frameIdx;\r\n const lastFrame = this._lastFr;\r\n const mathMin = Math.min;\r\n let frame;\r\n let nextFrame;\r\n if (kfsNum === 1) {\r\n frame = nextFrame = keyframes[0];\r\n }\r\n else {\r\n // In the easing function like elasticOut, percent may less than 0\r\n if (percent < 0) {\r\n frameIdx = 0;\r\n }\r\n else if (percent < this._lastFrP) {\r\n // Start from next key\r\n // PENDING start from lastFrame ?\r\n const start = mathMin(lastFrame + 1, kfsNum - 1);\r\n for (frameIdx = start; frameIdx >= 0; frameIdx--) {\r\n if (keyframes[frameIdx].percent <= percent) {\r\n break;\r\n }\r\n }\r\n frameIdx = mathMin(frameIdx, kfsNum - 2);\r\n }\r\n else {\r\n for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) {\r\n if (keyframes[frameIdx].percent > percent) {\r\n break;\r\n }\r\n }\r\n frameIdx = mathMin(frameIdx - 1, kfsNum - 2);\r\n }\r\n\r\n nextFrame = keyframes[frameIdx + 1];\r\n frame = keyframes[frameIdx];\r\n }\r\n\r\n // Defensive coding.\r\n if (!(frame && nextFrame)) {\r\n return;\r\n }\r\n\r\n this._lastFr = frameIdx;\r\n this._lastFrP = percent;\r\n\r\n const interval = (nextFrame.percent - frame.percent);\r\n let w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1);\r\n\r\n // Apply different easing of each keyframe.\r\n // Use easing specified in target frame.\r\n if (nextFrame.easingFunc) {\r\n w = nextFrame.easingFunc(w);\r\n }\r\n\r\n // If value is arr\r\n let targetArr = isAdditive ? this._additiveValue\r\n : (isValueColor ? tmpRgba : target[propName]);\r\n\r\n if ((isArrayValueType(valType) || isValueColor) && !targetArr) {\r\n targetArr = this._additiveValue = [];\r\n }\r\n\r\n if (this.discrete) {\r\n // use raw value without parse in discrete animation.\r\n target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue;\r\n }\r\n else if (isArrayValueType(valType)) {\r\n valType === VALUE_TYPE_1D_ARRAY\r\n ? interpolate1DArray(\r\n targetArr as NumberArray,\r\n frame[valueKey] as NumberArray,\r\n nextFrame[valueKey] as NumberArray,\r\n w\r\n )\r\n : interpolate2DArray(\r\n targetArr as NumberArray[],\r\n frame[valueKey] as NumberArray[],\r\n nextFrame[valueKey] as NumberArray[],\r\n w\r\n );\r\n }\r\n else if (isGradientValueType(valType)) {\r\n const val = frame[valueKey] as ParsedGradientObject;\r\n const nextVal = nextFrame[valueKey] as ParsedGradientObject;\r\n const isLinearGradient = valType === VALUE_TYPE_LINEAR_GRADIENT;\r\n target[propName] = {\r\n type: isLinearGradient ? 'linear' : 'radial',\r\n x: interpolateNumber(val.x, nextVal.x, w),\r\n y: interpolateNumber(val.y, nextVal.y, w),\r\n // TODO performance\r\n colorStops: map(val.colorStops, (colorStop, idx) => {\r\n const nextColorStop = nextVal.colorStops[idx];\r\n return {\r\n offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w),\r\n color: rgba2String(interpolate1DArray(\r\n [] as number[], colorStop.color, nextColorStop.color, w\r\n ) as number[])\r\n };\r\n }),\r\n global: nextVal.global\r\n };\r\n if (isLinearGradient) {\r\n // Linear\r\n target[propName].x2 = interpolateNumber(\r\n (val as ParsedLinearGradientObject).x2, (nextVal as ParsedLinearGradientObject).x2, w\r\n );\r\n target[propName].y2 = interpolateNumber(\r\n (val as ParsedLinearGradientObject).y2, (nextVal as ParsedLinearGradientObject).y2, w\r\n );\r\n }\r\n else {\r\n // Radial\r\n target[propName].r = interpolateNumber(\r\n (val as ParsedRadialGradientObject).r, (nextVal as ParsedRadialGradientObject).r, w\r\n );\r\n }\r\n }\r\n else if (isValueColor) {\r\n interpolate1DArray(\r\n targetArr,\r\n frame[valueKey] as NumberArray,\r\n nextFrame[valueKey] as NumberArray,\r\n w\r\n );\r\n if (!isAdditive) { // Convert to string later:)\r\n target[propName] = rgba2String(targetArr);\r\n }\r\n }\r\n else {\r\n const value = interpolateNumber(frame[valueKey] as number, nextFrame[valueKey] as number, w);\r\n if (isAdditive) {\r\n this._additiveValue = value;\r\n }\r\n else {\r\n target[propName] = value;\r\n }\r\n }\r\n\r\n // Add additive to target\r\n if (isAdditive) {\r\n this._addToTarget(target);\r\n }\r\n }\r\n\r\n private _addToTarget(target: any) {\r\n const valType = this.valType;\r\n const propName = this.propName;\r\n const additiveValue = this._additiveValue;\r\n\r\n if (valType === VALUE_TYPE_NUMBER) {\r\n // Add a difference value based on the change of previous frame.\r\n target[propName] = target[propName] + additiveValue;\r\n }\r\n else if (valType === VALUE_TYPE_COLOR) {\r\n // TODO reduce unnecessary parse\r\n color.parse(target[propName], tmpRgba);\r\n add1DArray(tmpRgba, tmpRgba, additiveValue as NumberArray, 1);\r\n target[propName] = rgba2String(tmpRgba);\r\n }\r\n else if (valType === VALUE_TYPE_1D_ARRAY) {\r\n add1DArray(target[propName], target[propName], additiveValue as NumberArray, 1);\r\n }\r\n else if (valType === VALUE_TYPE_2D_ARRAY) {\r\n add2DArray(target[propName], target[propName], additiveValue as NumberArray[], 1);\r\n }\r\n }\r\n}\r\n\r\n\r\ntype DoneCallback = () => void;\r\ntype AbortCallback = () => void;\r\nexport type OnframeCallback = (target: T, percent: number) => void;\r\n\r\nexport type AnimationPropGetter = (target: T, key: string) => InterpolatableType;\r\nexport type AnimationPropSetter = (target: T, key: string, value: InterpolatableType) => void;\r\n\r\nexport default class Animator {\r\n\r\n animation?: Animation\r\n\r\n targetName?: string\r\n\r\n scope?: string\r\n\r\n __fromStateTransition?: string\r\n\r\n private _tracks: Dictionary